目录
类继承的语法:
class DerivedClassName(BaseClassName)
BaseClass:被继承的类,可以称为基类、父类或超类;
DerivedClass:继承的类,可以称为子类,子类可以继承它的父类的所有属性和方法;
子类还可以在父类基础上添加新的属性和方法;
如子类定义与父类同名的属性或方法,则自动覆盖父类对应的属性或方法;
- #创建父类
- class FatherClass:
- father_name = 'David'
- father_info = 1234567890
- def father_sayhi(self):
- print(f'{self.father_name} say: HI!')
-
- #基于父类创建的实例对象,调用属性、方法正常
- ftest = FatherClass()
- ftest.father_name
- 'David'
- ftest.father_info
- 1234567890
- ftest.father_sayhi()
- David say: HI!
-
- #基于之前创建的父类创建子类,添加新的属性和方法,并修改原有属性和方法
- class SonClass(FatherClass):
- son_name = 'Dave'
-
- def son_sayhi(self):
- print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
-
- father_info = 9876543210
-
- def father_sayhi(self):
- print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
-
- #父类原有的属性调用正常
- #新添加的属性和方法调用正常
- #父类与子类相同的属性和方法,被子类修改后的新属性和方法覆盖
- stest = SonClass()
- stest.father_name
- 'David'
- stest.father_sayhi()
- David face to Dave say: 9876543210
- stest.son_sayhi()
- Dave face to David say: 9876543210
通过如下两个函数,可以做对象和类的从属判断,函数说明和语法结构如下:
isinstance():判断一个对象是否属于某个类;
issubclass():判断一个类是否属于某个类的子类;
- isinstance(object, classinfo, /)
-
- issubclass(class, classinfo, /)
子类创建的对象不仅属于子类,也属于创建对象类的父类;
父类创建的对象,则不属于子类;
- #子类创建的对象属于子类,也属于父类
- isinstance(stest,SonClass)
- True
- isinstance(stest,FatherClass)
- True
-
- #父类创建的对象只属于父类
- isinstance(ftest,SonClass)
- False
- isinstance(ftest,FatherClass)
- True
-
- #子类从属于父类,反过来则为 False
- issubclass(SonClass,FatherClass)
- True
- issubclass(FatherClass,SonClass)
- False
-
- #实例对象不能用于类的从属判断
- issubclass(stest,FatherClass)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- TypeError: issubclass() arg 1 must be a class
很多时候需要在子类中,直接调用父类方法;此时,子类中调用的父类方法的参数 self 是子类而不是父类的实例对象,即标题所说的未绑定即不需要绑定父类的实例对象,使用子类的实例对象代替即可;
需要注意的是,调用父类方法需要在子类方法中,否则无法使用 self 参数,传递子类创建的实例对象。
- #创建父类
- class FatherClass:
- def father_value(self,name,info):
- self.father_name = name
- self.father_info = info
- def father_sayhi(self):
- print(f'{self.father_name} say: HI!')
-
-
- #创建子类1:未调用父类的未绑定方法
- class SonClass(FatherClass):
- son_name = 'Dave'
-
- def son_sayhi(self):
- print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
-
- father_info = 9876543210
-
- def father_sayhi(self):
- print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
-
- #未调用父类初始化方法,部分变量未声明,调用报错
- stest = SonClass()
- stest.son_sayhi()
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- File "<input>", line 5, in son_sayhi
- AttributeError: 'SonClass' object has no attribute 'father_name'
- #此时需要调用父类的方法,赋值变量
- stest.father_value('David',1234)
- stest.son_sayhi()
- Dave face to David say: 1234
-
-
- #创建子类2:子类中调用未绑定的父类方法
- class SonClass(FatherClass):
- son_name = 'Dave'
-
- def son_sayhi(self):
- FatherClass.father_value(self, 'David2', 5678)
- print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
-
- father_info = 9876543210
-
- def father_sayhi(self):
- print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
-
- #直接在子类方法中完成父类的初始化函数
- stest2 = SonClass()
- stest.son_sayhi()
- Dave face to David say: 1234
很多时候,可能需要改变子类的继承关系、或者修改父类的名称,如子类中有大量对未绑定父类方法的调用,则需要修改大量代码;
此时,可以在调用父类方法时,使用 super 函数自动寻找父类方法,这样修改继承关系或父类名称时,无需对子类内部代码做改动;
需要注意,使用 super 函数调用父类方法,无需再在方法中输入 self 参数;
- #创建使用 super 函数的子类
- class SonClass(FatherClass):
- son_name = 'Dave'
-
- def son_sayhi(self):
- #使用 super() 代理父类名,方法中无需再输入 self 参数
- super().father_value('David3', 10000)
- print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
-
- father_info = 9876543210
-
- def father_sayhi(self):
- print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
-
-
- #直接在子类方法中完成父类的初始化函数
- stest3 = SonClass()
- stest3.son_sayhi()
- Dave face to David3 say: 10000
子类同时继承至多个父类的属性和方法,称之为多重继承,语法如下:
class DerivedClassName(BaseClassName1,BaseClassName2,BaseClassName3)
调用顺序一般从左至右,只有调用顺序靠前的类中查找不到相应属性或方法,才会继续往后查找;即,如果多个父类有重复属性或方法,则调用顺序靠前的被调用;
如果是多层级的父类相互嵌套、叠用,则可能会出现其他问题,参见多重继承陷阱 ,通常不建议在不确定时,使用多重继承;
可以通过 子类.__mro__ 查询子类的MRO调用顺序。
- #父类A
- class FatherA:
- fa_x = 'fax'
- fa_y = 'fay'
- def sayhi(self):
- print(f'{self.fa_x},{self.fa_y},hi!')
-
- #父类B
- class FatherB:
- fb_x = 'fbx'
- fb_y = 'fby'
- def sayhi(self):
- print(f'{self.fb_x},{self.fb_y},hi!')
-
- #多重继承的子类
- class Son(FatherB,FatherA):
- pass
-
-
- #子类可以调用所有父类的属性和方法
- #如上述调用顺序,优先调用父类B中 sayhi 方法
- stest = Son()
- stest.sayhi()
- fbx,fby,hi!
- stest.fa_x
- 'fax'
-
- #查询子类的MRO调用顺序
- Son.__mro__
- (<class '__main__.Son'>, <class '__main__.FatherB'>, <class '__main__.FatherA'>, <class 'object'>)
支持多继承的面向对象编程都可能会导致钻石继承(菱形继承)问题,如以下代码:
- class A():
- def __init__(self):
- print("进入A…")
- print("离开A…")
-
- class B(A):
- def __init__(self):
- print("进入B…")
- A.__init__(self)
- print("离开B…")
-
- class C(A):
- def __init__(self):
- print("进入C…")
- A.__init__(self)
- print("离开C…")
-
- class D(B, C):
- def __init__(self):
- print("进入D…")
- B.__init__(self)
- C.__init__(self)
- print("离开D…")
-
- >>> d = D()
- 进入D…
- 进入B…
- 进入A…
- 离开A…
- 离开B…
- 进入C…
- 进入A…
- 离开A…
- 离开C…
- 离开D…
多重继承容易导致钻石继承(菱形继承)问题,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。 ;
当出现多重继承时,Python 使用“方法解析顺序(Method Resolution Order,MRO)”,可以避免同一类被调用多次的前提下,使用广度优先和从左到右的原则去寻找需要的属性和方法;
使用 super() 函数,即按MRO顺序调用,可避免多重继承的陷阱,可以通过 子类.__mro__ 查询子类的MRO调用顺序。