1.  
  2. 主页
  3.  / 
  4. Python基础到高级
  5.  / 
  6. 面向对象
  7.  / 
  8. 继承

继承

类的继承

继承

继承可以获取父类的属性和方法,减少代码的重用

在Python3中,所有的类都是继承object的,它可以称之为是所有类的父类,object是可以省略的

父类:被继承的类,就是父类,也叫作基类或者超类

子类:继承父类的类,就是子类

In [1]: class Base():   # 父类
   ...:     def base_print(self):
   ...:         print('base')
   ...:         
 
In [4]: class A(Base):   # 在类名后面加上括号,括号中是继承列表,可继承多个类
   ...:     def a_print(self):
   ...:         print('a')
   ...:         

In [5]: a = A()

In [6]: a.a_print()
a

In [8]: a.base_print()   # 直接通过子类的实例化,来调用父类的方法
base

类的继承方式:

  • 凡是公有的方法或者属性,都是可以继承的
  • 凡是私有的方法或者属性,都是不能继承的
  • 原来的是什么,继承过来, 就还是什么

重写

当子类和父类有同名成员的时候,子类的成员会覆盖父类的同名成员

In [9]: class Base():
   ...:     def print(self):   
   ...:         print('Base.print')
   ...:         

In [10]: class Sub(Base):   # 继承Base父类
    ...:     def print(self):   # 父类和子类同时拥有print方法
    ...:         print('Sub.print')
    ...:         

In [11]: sub = Sub()   # 实例化子类

In [12]: sub.print()   # 当调用父类和子类的相同的方法的时候,则会调用子类的print方法
Sub.print

当重写后,有需求需要调用父类的同名方法的时候,可以使用super方法来调用

In [14]: class Sub(Base):   # 继承上面的Base类
    ...:     def print(self):  
    ...:         print('Sub.print')
    ...:         
    ...:     def foo(self):
    ...:         super().print()   # 使用super方法调用父类的print方法
    ...:         
    ...:         

In [15]: sub = Sub()

In [16]: sub.foo()   # 结果集显示的确实是父类的print方法的返回值
Base.print

当父类中含有一个带参数的初始化方法的时候,子类一定需要一个初始化方法,并且在初始化方法中调用父类的初始化方法

In [17]: class Base:    # 定义一个初始化方法中有带参数的父类
    ...:     def __init__(self, a, b):  
    ...:         self.__a = a
    ...:         self.__b = b
    ...:     def sum(self):
    ...:         return self.__a + self.__b
    ...:     

In [18]: class Sub(Base): 
    ...:     def __init__(self,a,b,c):    
    ...:         self.c = c
    ...:         super().__init__(a,b)  # 给父类传参,这个必须要有的,要不然就报错
    ...:         

In [19]: sub = Sub(1,2,3)

In [20]: sub.sum()
Out[20]: 3

super获取类变量

In [24]: class Base:
    ...:     NAME = 'BASE'
    ...:     

In [25]: class Sub(Base):
    ...:     NAME = 'SUB'
    ...:     def print(self):
    ...:         print(self.NAME)
    ...:         print(super(Sub,Sub).NAME)   # 获取父类的属性
    ...:         

In [26]: sub = Sub()

In [27]: sub.print()
SUB
BASE

super对象只能获取类的属性,是不能获取类实例的属性或者方法的

多继承

多继承的表达形式

In [2]: class Base:
   ...:     pass
   ...: 

In [3]: class Base1:
   ...:     pass
   ...: 

In [4]: class Sub2(Base, Base1):   # 多继承,在继承列表中存在了多个类的时候,表示多继承
   ...:     pass
   ...: 

多继承会把继承列表中的所有的公有成员都继承过来

在多继承中,会出现继承列表中的方法重名的情况,因此就有一个MRO(方法查找顺序)

MRO两条特性

  • 本地优先:自己定义或者重写的方法优先,按照继承列表,从左到右查找
  • 单调性:所有子类也要满足查找顺序

按照继承列表,从左到右查找调用

In [13]: class A:
    ...:     def print(self):
    ...:         print('a')
    ...:         

In [14]: 

In [14]: class B:
    ...:     def print(self):
    ...:         print('b')
    ...:         
    ...:         

In [15]: 

In [15]: class C(A,B):
    ...:     pass
    ...: 

In [16]: c = C()

In [17]: c.print()   # print是继承列表中的重名方法,因为先继承的A因此,会先调用A的print方法
a

本地优先

In [5]: class C(A,B):
   ...:     def print(self):   # 因为在当前类中也存在print方法,因此会首先调用当前类中的print方法
   ...:         print('c')
   ...:         

In [6]: c = C()

In [7]: c.print()
c

单调性,每个类都有一个__mro__方法,可以查看类方法查找的顺序

In [8]: C.__mro__
Out[8]: (__main__.C, __main__.A, __main__.B, object)
从左到右,根据首先查找本地时候有重名的方法,然后根据继承的列表来查询方法,最后继承object

在python中是通过C3算法来确定是否满足mro的两个原则的

当一个类定义的时候,解释器会执行C3算法来确定mro。如果C3算法抛出异常,则此类不能定义

在我们的开发中,要尽量的避免使用多继承,因为多继承特别容易出现代码的混乱,当代码的继承出现多层的时候,就会特别容易出错

多继承会对程序的心智负担造成非常大的压力

Mixin

Mixin其实就是一种用组合的方式,将多个父类的方法糅合到子类中,让子类可以无缝的调用父类的方法

Mixin的组合方式,在理论上来说是优于继承的

当然Mixin也是有限制的

  • Mixin方式的类不应该有初始化方法,因为是组合的因此,Mixin类重尽量不要有初始化方法,因为胡覆盖掉父类的初始化方法,发生不必须的错误
  • Mixin方式的类不能独立工作,当Mixin方式的类,在独立的情况下,是无法工作的,如果可以工作,就已经不属于Mixin方式的类的范畴了
  • Mixin方式的类的祖先也必须是Mixin的方式的类

通常情况下,Mixin类总是在继承列表的第一位

个人觉得还是尽量别使用Mixin方式来组合方法称为一个新的类来使用,因为当组合类越来越多的时候,就管理和排错我们的程序了,因此如何真的觉得Mixin方式非常适合现在的应用场景的话,在去使用,一般情况下,其实装饰器也是可以实现Mixin这样方式的组合类的,装饰器更方便与查找和管理。

这篇文章对您有用吗?

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注