• 类和对象2:继承


    目录

    1. 类继承基础

    2. 对象和类的从属

    3. 调用未绑定父类方法

    4. super函数:子类中替代父类名

    5. 多重继承

    6. 多重继承陷阱


    1. 类继承基础

    类继承的语法:

    class DerivedClassName(BaseClassName)

    BaseClass:被继承的类,可以称为基类、父类或超类;

    DerivedClass:继承的类,可以称为子类,子类可以继承它的父类的所有属性和方法;

    子类还可以在父类基础上添加新的属性和方法;

    如子类定义与父类同名的属性或方法,则自动覆盖父类对应的属性或方法;

    1. #创建父类
    2. class FatherClass:
    3. father_name = 'David'
    4. father_info = 1234567890
    5. def father_sayhi(self):
    6. print(f'{self.father_name} say: HI!')
    7. #基于父类创建的实例对象,调用属性、方法正常
    8. ftest = FatherClass()
    9. ftest.father_name
    10. 'David'
    11. ftest.father_info
    12. 1234567890
    13. ftest.father_sayhi()
    14. David say: HI!
    15. #基于之前创建的父类创建子类,添加新的属性和方法,并修改原有属性和方法
    16. class SonClass(FatherClass):
    17. son_name = 'Dave'
    18. def son_sayhi(self):
    19. print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
    20. father_info = 9876543210
    21. def father_sayhi(self):
    22. print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
    23. #父类原有的属性调用正常
    24. #新添加的属性和方法调用正常
    25. #父类与子类相同的属性和方法,被子类修改后的新属性和方法覆盖
    26. stest = SonClass()
    27. stest.father_name
    28. 'David'
    29. stest.father_sayhi()
    30. David face to Dave say: 9876543210
    31. stest.son_sayhi()
    32. Dave face to David say: 9876543210

    2. 对象和类的从属

    通过如下两个函数,可以做对象和类的从属判断,函数说明和语法结构如下:

    isinstance():判断一个对象是否属于某个类; 

    issubclass():判断一个类是否属于某个类的子类;

    1. isinstance(object, classinfo, /)
    2. issubclass(class, classinfo, /)

    子类创建的对象不仅属于子类,也属于创建对象类的父类;

    父类创建的对象,则不属于子类;

    1. #子类创建的对象属于子类,也属于父类
    2. isinstance(stest,SonClass)
    3. True
    4. isinstance(stest,FatherClass)
    5. True
    6. #父类创建的对象只属于父类
    7. isinstance(ftest,SonClass)
    8. False
    9. isinstance(ftest,FatherClass)
    10. True
    11. #子类从属于父类,反过来则为 False
    12. issubclass(SonClass,FatherClass)
    13. True
    14. issubclass(FatherClass,SonClass)
    15. False
    16. #实例对象不能用于类的从属判断
    17. issubclass(stest,FatherClass)
    18. Traceback (most recent call last):
    19. File "<input>", line 1, in <module>
    20. TypeError: issubclass() arg 1 must be a class

    3. 调用未绑定父类方法

    很多时候需要在子类中,直接调用父类方法;此时,子类中调用的父类方法的参数 self 是子类而不是父类的实例对象,即标题所说的未绑定即不需要绑定父类的实例对象,使用子类的实例对象代替即可;

    需要注意的是,调用父类方法需要在子类方法中,否则无法使用 self 参数,传递子类创建的实例对象。

    1. #创建父类
    2. class FatherClass:
    3. def father_value(self,name,info):
    4. self.father_name = name
    5. self.father_info = info
    6. def father_sayhi(self):
    7. print(f'{self.father_name} say: HI!')
    8. #创建子类1:未调用父类的未绑定方法
    9. class SonClass(FatherClass):
    10. son_name = 'Dave'
    11. def son_sayhi(self):
    12. print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
    13. father_info = 9876543210
    14. def father_sayhi(self):
    15. print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
    16. #未调用父类初始化方法,部分变量未声明,调用报错
    17. stest = SonClass()
    18. stest.son_sayhi()
    19. Traceback (most recent call last):
    20. File "<input>", line 1, in <module>
    21. File "<input>", line 5, in son_sayhi
    22. AttributeError: 'SonClass' object has no attribute 'father_name'
    23. #此时需要调用父类的方法,赋值变量
    24. stest.father_value('David',1234)
    25. stest.son_sayhi()
    26. Dave face to David say: 1234
    27. #创建子类2:子类中调用未绑定的父类方法
    28. class SonClass(FatherClass):
    29. son_name = 'Dave'
    30. def son_sayhi(self):
    31. FatherClass.father_value(self, 'David2', 5678)
    32. print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
    33. father_info = 9876543210
    34. def father_sayhi(self):
    35. print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
    36. #直接在子类方法中完成父类的初始化函数
    37. stest2 = SonClass()
    38. stest.son_sayhi()
    39. Dave face to David say: 1234

    4. super函数:子类中替代父类名

    很多时候,可能需要改变子类的继承关系、或者修改父类的名称,如子类中有大量对未绑定父类方法的调用,则需要修改大量代码;

    此时,可以在调用父类方法时,使用 super 函数自动寻找父类方法,这样修改继承关系或父类名称时,无需对子类内部代码做改动;

    需要注意,使用 super 函数调用父类方法,无需再在方法中输入 self 参数;

    1. #创建使用 super 函数的子类
    2. class SonClass(FatherClass):
    3. son_name = 'Dave'
    4. def son_sayhi(self):
    5. #使用 super() 代理父类名,方法中无需再输入 self 参数
    6. super().father_value('David3', 10000)
    7. print(f'{self.son_name} face to {self.father_name} say: {self.father_info}')
    8. father_info = 9876543210
    9. def father_sayhi(self):
    10. print(f'{self.father_name} face to {self.son_name} say: {self.father_info}')
    11. #直接在子类方法中完成父类的初始化函数
    12. stest3 = SonClass()
    13. stest3.son_sayhi()
    14. Dave face to David3 say: 10000

    5. 多重继承

    子类同时继承至多个父类的属性和方法,称之为多重继承,语法如下:

    class DerivedClassName(BaseClassName1,BaseClassName2,BaseClassName3)

    调用顺序一般从左至右,只有调用顺序靠前的类中查找不到相应属性或方法,才会继续往后查找;即,如果多个父类有重复属性或方法,则调用顺序靠前的被调用;

    如果是多层级的父类相互嵌套、叠用,则可能会出现其他问题,参见多重继承陷阱 ,通常不建议在不确定时,使用多重继承;

    可以通过 子类.__mro__ 查询子类的MRO调用顺序。

    1. #父类A
    2. class FatherA:
    3. fa_x = 'fax'
    4. fa_y = 'fay'
    5. def sayhi(self):
    6. print(f'{self.fa_x},{self.fa_y},hi!')
    7. #父类B
    8. class FatherB:
    9. fb_x = 'fbx'
    10. fb_y = 'fby'
    11. def sayhi(self):
    12. print(f'{self.fb_x},{self.fb_y},hi!')
    13. #多重继承的子类
    14. class Son(FatherB,FatherA):
    15. pass
    16. #子类可以调用所有父类的属性和方法
    17. #如上述调用顺序,优先调用父类B中 sayhi 方法
    18. stest = Son()
    19. stest.sayhi()
    20. fbx,fby,hi!
    21. stest.fa_x
    22. 'fax'
    23. #查询子类的MRO调用顺序
    24. Son.__mro__
    25. (<class '__main__.Son'>, <class '__main__.FatherB'>, <class '__main__.FatherA'>, <class 'object'>)

    6. 多重继承陷阱

    支持多继承的面向对象编程都可能会导致钻石继承(菱形继承)问题,如以下代码:

    1. class A():
    2. def __init__(self):
    3. print("进入A…")
    4. print("离开A…")
    5. class B(A):
    6. def __init__(self):
    7. print("进入B…")
    8. A.__init__(self)
    9. print("离开B…")
    10. class C(A):
    11. def __init__(self):
    12. print("进入C…")
    13. A.__init__(self)
    14. print("离开C…")
    15. class D(B, C):
    16. def __init__(self):
    17. print("进入D…")
    18. B.__init__(self)
    19. C.__init__(self)
    20. print("离开D…")
    21. >>> d = D()
    22. 进入D…
    23. 进入B…
    24. 进入A…
    25. 离开A…
    26. 离开B…
    27. 进入C…
    28. 进入A…
    29. 离开A…
    30. 离开C…
    31. 离开D…

     多重继承容易导致钻石继承(菱形继承)问题,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。 ;

    当出现多重继承时,Python 使用“方法解析顺序(Method Resolution Order,MRO)”,可以避免同一类被调用多次的前提下,使用广度优先和从左到右的原则去寻找需要的属性和方法;

    使用 super() 函数,即按MRO顺序调用,可避免多重继承的陷阱,可以通过 子类.__mro__ 查询子类的MRO调用顺序。

  • 相关阅读:
    mq的那些问题,你都整明白了?
    2022-36~37周(8.29-9.11) 项目问题整理
    C# 中 i++ 和 ++i的区别
    小程序:去掉页面的橡皮筋效果
    Open3D 进阶(18)整体最小二乘拟合平面
    centos下安装jenkins.war
    70. 爬楼梯
    ES6知识查漏补缺(Math对象,作用域,Object.is())
    Netty核心组件源码说明
    【机器学习】scikitLearn正则化l1,l2,提前停止
  • 原文地址:https://blog.csdn.net/davidksatan/article/details/125467034