派生
派生
子类中新定义的属性的这个过程叫做派生,子类在使用派生的属性时始终以自己的为准。
class A1:
x = 10
class B1(A1):
x = 11 # 派生属性
def f1(self): # 派生方法
pass
b = B1()
print(b.x)
11
派生类中使用父类的属性
- 方式一:self,但这样如果子类中有相同属性,则会优先使用子类的属性,依赖继承关系。
class A1:
def f1(self):
print('AAAA')
class B1(A1):
def f2(self):
self.f1()
b = B1()
b.f2()
AAA
- 方式二:指明道姓使用父类的属性,可以不依赖继承关系。
class A1:
def f1(self):
print('AAAA')
class B1:
def f2(self):
A1.f1(self) # 通过类调用方法,此时为普通函数,需要向self传参。
b = B1()
b.f2()
AAAA
- 方式三:使用内置函数super(),严格依赖继承关系。
super([type[, object-or-type]])
返回一个代理对象,该对象会参照发起属性查找的那个类的mro列表,去super当前所在类的父类中查找属性。即便并没有直接继承关系,super仍然会按照MRO列表继续往后查找。super不会从对象的名称空间和对象所属的父类内查找属性,而是直接从MRO列表的下一个类中查找。
在Python2中super的使用需要完整地写成super(自己的类名,self) ,而在python3中可以简写为super()。
class A1:
def f1(self):
print('AAAA')
class B1(A1):
def f2(self):
# 通过对象调用绑定方法会将对象传给self。
super().f1()
b = B1()
b.f2()
使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
class A:
def test(self): # 对象obj和类C均没有test方法则会在父类中查找
print('from A')
super().test() # super会参照发起属性查找的类的mro列表,去super当前所在类的父类中查找属性。即便A和B并没有继承关系。
class B:
def test(self):
print('from B')
class C(A, B):
pass
print(C.mro())
obj = C()
obj.test()
super会参照发起属性查找的类的mro列表,去super当前所在类的父类中查找属性。即便A和B并没有继承关系,也会去B中查找。
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
from A
from B
更推荐使用super()来重用父类的方法,但不要同时使用两种方法。
类的特殊属性
#python为类内置的特殊属性
类名.__name__ # 类的名字(字符串)
类名.__doc__ # 类的文档字符串
类名.__base__ # 类的第一个父类
类名.__bases__ # 类所有父类构成的元组,仅查看直接父类。
类名.__dict__ # 类的属性字典
类名.__module__ # 类定义所在的模块
类名.__class__ # 实例对应的类(仅新式类中)
取值顺序
单继承:当前对象的名称空间 --> 子类名称空间 --> 父类 --> 父类的父类... --> object
class A1:
def f1(self):
print('A1.f1')
def f2(self):
print('A1.f2')
self.f1()
class B1(A1):
def f1(self):
print('B1.f1')
b = B1()
b.f2()
对象b会先在自身名称空间内查找,然后是子类,然后在父类内找到后即会执行f2(),第一行打印 ‘ A1.f2 ’ ,第二行又调用self.f1(),此时self为对象b,同样会先遵循取值顺序,对象 -》子类 -》父类 -》object。
A1.f2
B1.f1
多继承会有个取值问题,也就是当多个父类有相同属性的时候,会从哪里取值,这就是后面要说的菱形问题。