本文目录一览:
- 1、python继承父类 怎么引用
- 2、Python的类和对象入门
- 3、Python程序中调用子类时直接修改父类的属性
- 4、Python继承父类parent的正确格式为
- 5、Python类的多重继承问题深入分析
- 6、python 多重继承,继承的几个父类都需要传递参数,怎么在子类计算出父类传递的参数总和呢?
python继承父类 怎么引用
子类调用父类函数有以下方法:
直接写类名调用
用 super(type, obj).method(arg)方法调用。
在类定义中调用本类的父类方法,可以直接用super().method(arg)
1
2
3
4
5
6
7
8
9
class A:
def method(self, arg):
pass
class B(A):
def method(self, arg):
# A.method(self,arg) # 1
# super(B, self).method(arg) # 2
super().method(arg) # 3
Python的类和对象入门
本文来说说Python中的类与对象,Python这门语言是无处不对象,如果你曾浅要了解过Python,你应该听过Python是一种面向对象编程的语言,所以你经常可能会看到面向“对象”编程这类段子,而面向对象编程的语言都会有三大特征:封装、继承、多态。
我们平时接触到的很多函数、方法的操作都具有这些性质,我们只是会用,但还没有去深入了解它的本质,下面就介绍一下关于类和对象的相关知识。
封装这个概念应该并不陌生,比如我们把一些数据封装成一个列表,这就属于数据封装,我们也可以将一些代码语句封装成一个函数方便调用,这就是代码的封装,我们也可以将数据和代码封装在一起。用术语表示的话,就是可以将属性和方法进行封装,从而得到对象。
首先我们可以定义一个类,这个类中有属性和方法,但有的伙伴会比较好奇,属性和方法不是会封装成对象嘛,为什么又变成类了?举个例子,类就好比是一个毛坯房,而对象是在毛坯房的基础上改造成的精装房。
在类定义完成时就创建了一个类对象,它是对类定义创建的命名空间进行了一个包装。类对象支持两种操作:属性引用和实例化。
属性引用的语法就是一般的标准语法:obj.name。比如XiaoMing.height和XiaoMing.run就是属性引用,前者会返回一条数据,而后者会返回一个方法对象。
这里也支持对类属性进行赋值操作,比如为类中的weight属性赋予一个新值。
而类的实例化可以将类对象看作成一个无参函数的赋值给一个局部变量,如下:
ming就是由类对象实例化后创建的一个实例对象,通过实例对象也可以调用类中的属性和方法。
类在实例化过程中并不都是像上面例子一样简单的,一般类都会倾向将实例对象创建为有初始状态的,所以在类中可能会定义一个__init__的魔法方法,这个方法就可以帮助接收、传入参数。
而一个类如果定义了__init__方法,那么在类对象实例化的过程中就会自动为新创建的实例化对象调用__init__方法,请看下面这个例子。
可以看到在__init__()中传入了参数x和y,然后在print_coor中需要接收参数x和y,接下来通过实例化这个类对象,验证一下参数是否能通过__init__()传递到类的实例化操作中。
所谓继承就是一个新类在另一个类的基础上构建而成,这个新类被称作子类或者派生类,而另一个类被称作父类、基类或者超类,而子类会继承父类中已有的一些属性和方法。
比如上面这个例子,我并没有将list_定义成一个列表,但它却能调用append方法。原因是类Mylist继承于list这个基类,而list_又是Mylist的一个实例化对象,所以list_也会拥有父类list拥有的方法。当然可以通过自定义类的形式实现两个类之间的继承关系,我们定义Parent和Child两个类,Child中没有任何属性和方法,只是继承于父类Parent。
当子类中定义了与父类中同名的方法或者属性,则会自动覆盖父类对应的方法或属性,还是用上面这个例子实现一下,方便理解。
可以看到子类Child中多了一个和父类Parent同名的方法,再实例化子类并调用这个方法时,最后调用的是子类中的方法。Python中继承也允许多重继承,也就是说一个子类可以继承多个父类中的属性和方法,但是这类操作会导致代码混乱,所以大多数情况下不推荐使用,这里就不过多介绍了。
多态比较简单,比如定义两个类,这两个类没有任何关系,只是两个类中有同名的方法,而当两个类的实例对象分别调用这个方法时,不同类的实例对象调用的方法也是不同的。
上面这两个类中都有introduce方法,我们可以实例化一下两个类,利用实例对象调用这个方法实现一下多态。
判断一个类是否是另一个类的子类,如果是则返回True,反之则返回False。
需要注意的有两点:
判断一个对象是否为一个类的实例对象,如果是则返回True,反之则返回False。
需要注意的有两点:
判断一个实例对象中是否包含一个属性,如果是则返回True,反之则返回False。
需要注意的是第二个参数name必须为字符串形式传入,如果不是则会返回False。
Python程序中调用子类时直接修改父类的属性
在学习类的继承时,被“在子类中调用和修改父类的属性”卡住了。通过在网上查阅资料,大致搞明白了。主要有以下三点:
1. 子类的 __init__ 方法中通过“ 父类名.__init__(self) ”语句将父类的属性继承过来,见下面示例中标注“#例2”处。
2. 如果要在程序中调用子类时直接通过参数的形式修改父类继承过来的属性,那么子类的 __init__ 方法中需要在self后面跟上要赋值的参数,见下面示例中标注“#例1”处。
3. 即使是通过“ 父类名.__init__(self) ”语句继续过来的父类的属性,也需要在子类的 __init__ 方法中使用 self.变量名=变量值 的方式进行赋值,否则在子类中无法引用变量,见下面示例中标注“#例3”和“#例4”处。
执行结果:
从以上执行结果看,我们通过 y=b(4,5) 方式直接调用子类并给出参数“4,5”,而使用 y.myprint 调用了父类的方法,最后的计算结果是根据子类的参数计算得出的。说明我们在调用子类时直接修改了父类aa和bb的属性。
Python继承父类parent的正确格式为
格式:
class 子类名(父类1,父类2)
类的继承就是让子类拥有父类的属性和方法。
几个注意:py支持多继承
子类继承的父类只能初始化一次,如果父类1和父类2有共同的父类或者祖先类,则类初始化的时候会失败。
当父类具有相同方法时,会调用最先继承的父类中的方法,如果要指定父类,则需要重写此方法,并通过父类名.方法名来调用指定父类方法。
Python类的多重继承问题深入分析
Python类的多重继承问题深入分析
首先得说明的是,Python的类分为经典类 和 新式类
经典类是python2.2之前的东西,但是在2.7还在兼容,但是在3之后的版本就只承认新式类了
新式类在python2.2之后的版本中都可以使用
经典类和新式类的区别在于:
经典类是默认没有派生自某个基类的,而新式类是默认派生自object这个基类的:
代码如下:
# old style
class A():pass
# new style
class A(obejct):pass
2.经典类在类多重继承的时候是采用从左到右深度优先原则匹配方法的..而新式类是采用C3算法(不同于广度优先)进行匹配的
3.经典类是没有__MRO__和instance.mro()调用的,而新式类是有的.
为什么不用经典类,要更换到新式类
因为在经典类中的多重继承会有些问题...可能导致在继承树中的方法查询绕过后面的父类:
代码如下:
class A():
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass
d = D()
d.foo1()
按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过.
所以python引入了新式类的概念,每个基类都继承自object并且,他的匹配规则也从深度优先换到了C3
C3算法
C3算法是怎么做匹配的呢..在问答版块上面讨论之后,归结如下:
C3算法的一个核心是merge.
在merge列表中,如果第一个序列mro的第一个类是出现在其它序列,并且也是第一个,或者不出现其它序列,那么这个类就会从这些序列中删除,并合到访问顺序列表中
比如:(引用问题中zhuangzebo的回答@zhuangzebo)
代码如下:
class A(O):pass
class B(O):pass
class C(O):pass
class D(A,B):pass
class E(C,D):pass
首先需要知道 O(object)的mro(method resolution order)列表是[O,]
那么接下来是:
代码如下:
mro(A) = [A, O]
mro(B) = [B, O]
mro(C) = [C, O]
mro(D) = [D] + merge(mro(A), mro(B), [A, B])
= [D] + merge([A, O], [B, O], [A, B])
= [D, A] + merge([O], [B, O], [B])
= [D, A, B] + merge([O], [O])
= [D, A, B, O]
mro(E) = [E] + merge(mro(C), mro(D), [C, D])
= [E] + merge([C, O], [D, A, B, O], [C, D])
= [E, C] + merge([O], [D, A, B, O], [D])
= [E, C, D] + merge([O], [A, B, O])
= [E, C, D, A, B] + merge([O], [O])
= [E, C, D, A, B, O]
然后还有一种特殊情况:
比如:
merge(DO,CO,C) 先merge的是D
merge(DO,CO,C) 先merge的是C
意思就是.当出现有 一个类出现在两个序列的头(比如C) 这种情况和 这个类只有在一个序列的头(比如D) 这种情况同时出现的时候,按照顺序方式匹配。
新式类生成的访问序列被存储在一个叫MRO的只读列表中..
你可以使用instance.__MRO__或者instance.mro()来访问
最后匹配的时候就按照MRO序列的顺序去匹配了
C3和广度优先的区别:
举个例子就完全明白了:
代码如下:
class A(object):pass
class B(A):pass
class C(B):pass
class D(A):pass
class E(D):pass
class F(C, E):pass
按照广度优先遍历,F的MRO序列应该是[F,C,E,B,D,A]
但是C3是[F,E,D,C,B,A]
意思是你可以当做C3是在一条链路上深度遍历到和另外一条链路的交叉点,然后去深度遍历另外一条链路,最后遍历交叉点
新式类和经典类的super和按类名访问问题
在经典类中,你如果要访问父类的话,是用类名来访问的..
代码如下:
class A():
def __init__(self):
print "A"
class B(A):
def __init__(self):
print "B"
A.__init__(self) #python不会默认调用父类的初始化函数的
这样子看起来没三问题,但是如果类的继承结构比较复杂,会导致代码的可维护性很差..
所以新式类推出了super这个东西...
代码如下:
class A():
def __init__(self):
print "A"
class B(A):
def __init__(self):
print "B"
super(B,self).__init__()
这时候,又有一个问题:当类是多重继承的时候,super访问的是哪一个类呢?
super实际上是通过__MRO__序列来确定访问哪一个类的...实际上就是调用__MRO__中此类后面的一个类的方法.
比如序列为[F,E,D,C,B,A]那么F中的super就是E,E的就是D
super和按照类名访问 混合使用带来的坑
代码如下:
class A(object):
def __init__(self):
print "enter A"
print "leave A"
class B(object):
def __init__(self):
print "enter B"
print "leave B"
class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
B.__init__(self)
C.__init__(self)
print "leave E"
class F(E, D):
def __init__(self):
print "enter F"
E.__init__(self)
D.__init__(self)
print "leave F"
这时候打印出来是:
代码如下:
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
可以看出来D和A的初始化函数被乱入了两次!
按类名访问就相当于C语言之前的GOTO语句...乱跳,然后再用super按顺序访问..就有问题了
所以建议就是要么一直用super,要么一直用按照类名访问
最佳实现:
避免多重继承
super使用一致
不要混用经典类和新式类
调用父类的时候注意检查类层次
以上便是本人对于python类的继承的认识了,希望对大家能有所帮助
python 多重继承,继承的几个父类都需要传递参数,怎么在子类计算出父类传递的参数总和呢?
运行你的代码:出错位置: c = C()
出错结果:TypeError: __init__() missing 1 required positional argument: ' num1 '
先来看你的程序__main()__部分:a = A(2) 和 b = B(5) 这是类A和类B的一个实例。在python中实例变量是用于每个实例的唯一数据,这就说明你这里的传递参数2或者是5只能用在实例化的 a 或者是 b 下才有作用。 那么重点看 c = C( ) ,c是类对象C的实例化,c 只能用自身实例变量才有用,因此前面的实例 a 下的变量 num1=2 和 实例 b 下的变量 num1=5对实例c是无用的。所以,出错结果很明显了缺少传递的位置参数了。这为什么提示缺少1个位置参数呢?下面为你简单讲解一下吧。你也可以用内置方法__mro__() :查看方法或者属性的调用路径——print(类名.__mro__)
类C是多继承类A和类B的,多继承(不存在super()重写方法下),类C的实例化c是怎么工作的——对于实例c调用方法或属性过程是这样的:查找当前类C中是否存在,然后在多个父类中按照从左往右顺序查找(类A中先查找,类B中后查找),只要在这个过程中找到就退出了,后面的就不再查找了。
好吧,给你分析一下你程序的过程:类A和类B中都有__init__()同一个方法,方法相同那首先就查找呗——先查找类C(没有对__init__()进行修改,那就是跳过了),然后再去类A查找,好嘛这就找到了__init__(self, num1),找到了就退出了。所以这样一看对类C进行实例化就需要传递一个参数给num1就够了。你也可以交换继承位置class C(B, A),这样就是类C实例化需要传递一个参数给num2就够了。这样过程就清晰了。
好第三个问题来了:你类C中有两个参数呀num1和num2,而实例化又仅需要一个参数就够了,这样就肯定会产生问题了。
不信你试试给c = C(2)产生错误:AttributeError: 'C' object has no attribute 'num2'
解决方法1:既然没有属性num2就在类C中删掉就是了,然后c = C(2)就可以运行成功了。
解决方案2:类变量用于类的所有实例共享的属性和方法。因此,要想共用这两个变量num1和num2,就得让搜索的时候不要进到类A和类B中前提下,将它们变成对应的类变量就可以了。第一个前提很好实现:在类C下 定义:def __init__(self) : pass 第二个条件也比较好实现:将类A或类B的 __init__(self, num) : X.num = num X为对应的类名。(说明:self表示类实例化对象,即self.num 表示实例变量;X表示类对象,则X.num表示类变量),这样就可以共享类A和类B的变量了。
class A:
def __init__(self, num1):
A.num1 = num1
class B:
def __init__(self, num2):
B.num2 = num2
class C(A, B):
def __init__(self):
pass
def num_sum(self):
return self.num2 + self.num1
if __name__ == '__main__':
a = A(2)
b = B(5)
c = C()
print(c.num_sum())