python提高1 继承 单继承,多继承,mro

资料参考
1 多继承中的问题
https://blog.csdn.net/zsh142537/article/details/82685721

2 C3算法
https://blog.csdn.net/fmblzf/article/details/52512145

3 mro查找方法
https://www.bilibili.com/video/av50691919?from=search&seid=1714314693359442856

1.单继承

1.1 方法重写(覆盖)

  1. 在B类中有一个和父类A同名同参数的方法。
  2. 此时在B类里或者B的实例对象调用的fun方法是B里定义的fun。
    实例.fun()
    类名.fun(实例对象)
class A(object):
    def fun(self):
        print("A的fun方法")

class B(A):
    def fun(self):
        print("B的fun方法")

# 重写后直接调用的是父类方法
#通过对象调用方法
b=B()
b.fun()
#通过类调用方法
B.fun(b)

调用的是子类重写后的方法

B的fun方法
B的fun方法

1.2 在类中调用父类的方法

  1. 有三种调用方法
    (1)父类名.函数名(参数)
    (2)super().函数名(参数)
    (3)super(本类的类名,self).函数名(参数)
  2. 在类内部调用父类里被重写的fun方法
class A(object):
    def fun(self):
        print("A的fun方法")

class B(A):
    def fun(self):
        print("B的fun方法")
    def get_A_fun(self):
        A.fun(self)

class C(A):
    def fun(self):
        print("C的fun方法")
    def get_A_fun(self):
        super(C, self).fun()

class D(A):
    def fun(self):
        print("D的fun方法")
    def get_A_fun(self):
        super().fun()



# 子类内部访问父类方法
b=B()
b.fun()
b.get_A_fun()
c=C()
c.get_A_fun()
d=D()
d.get_A_fun()

访问的是父类内部的方法

A的fun方法
A的fun方法
A的fun方法

1.3 单继承中的构造方法问题

  1. 构造方法是一种特殊的方法,他和其他方法一样,如果父类构造方法被子类重写了,创建子类对象时就会自动调用子类构造方法。没有被重写,创建子类对象时会调用父类的构造方法。
  2. 创建普通对象调用构造方法时会开辟内存空间。在子类里可以显式调用父类的构造方法,但是不会开辟多余的内存空间,只会简单地执行__init__里面地语句。
class A(object):
    def __init__(self):
        print("父类的构造函数被调用")


class B(A):
    pass


class C(A):
    def __init__(self):
        print("子类的构造函数被调用")
    def fun(self):
        A.__init__(self)

A()
print("---")
B()
print("---")
C().fun()

执行结果

父类的构造函数被调用
---
父类的构造函数被调用
---
子类的构造函数被调用
父类的构造函数被调用

2 多继承中的问题

多继承中最简单也是最经典的继承模式,钻石继承。在这里插入图片描述

2.1 用类名直接调用父类的构造方法

用类名调用会发现有的方法被执行了多次。

class Parent(object):
    def __init__(self, name):
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age):
        Parent.__init__(self, name)
        self.age = age
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender):
        Parent.__init__(self, name)
        self.gender = gender
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        Son1.__init__(self, name, age)  # 单独调用父类的初始化方法
        Son2.__init__(self, name, gender)
        print('Grandson的init结束被调用')


gs = Grandson('grandson', 12, '男')

Grandson开始-Son1开始-Parent开始-Parent结束-Son1结束-Son2开始-Parent开始-Parent结束-Son2结束-Grandson结束。

parent的init结束被调用
Son1的init结束被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用

2.2 用super调用父类的构造方法

  1. 构造方法被执行了多次,显然是不合理的,用super().__init__方法可以确保每个构造方法只执行一遍。
  2. 这个执行顺序是根据就是mro「方法解析顺序」(Method Resolution Order).在2.7中它采用的是深度优先遍历,在2.7以上,采用的就是C3算法(稍后介绍)确定的。
class Parent(object):
    def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        self.age = age
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        self.gender = gender
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
        # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
        super().__init__(name, age, gender)
        print('Grandson的init结束被调用')


gs = Grandson('grandson', 12, '男')
print(Grandson.__mro__)

结果是

parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
  1. 此处根据mro序列学习super的调用父类构造方法的原理。
    通过两种方法可以查看mro序列
    print(Grandson.mro)
    print(Grandson.mro())
[<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>]

(1)在Grandson里__init__里,查看mro序列,super调用Grandson类后面的类Son1类的构造方法.
(2)Son1类里__init__里,查看mro序列,super调用Son1类后面的类Son2类的构造方法.
(3)Son2里的__init__里,super调用Son2类后面的类Parent类的构造方法.

2.3 用super传入参数调用父类的构造方法

  1. super(类名1,self).__init__方法,在自己的mro序列里找到类名1后面的那个类的构造方法。
  2. super(自己的类名,self)与super()效果相同。

2.4 三种调用的比较

  1. 类名直接调用,可能会调用多次
  2. super()在mro序列里找到当前类名后一个类调用其构造方法
  3. super(类名,self)在mro序列里找打指定类名后的第一个类调用其构造方法。

2.5 总结

  1. super一般是为了继承中构造函数的问题而进行的调用,通常出现在构造方法里。
  2. 普通函数的调用最好还是用类名直接调用。这样比较直接

3 MRO(Method Resolution Order):方法解析顺序

3.1 python 2.2版本之前经典类

所有经典类默认不继承object,并且采用的是深度优先算法。例如上面的钻石继承,在2.7之前深度优先序列就是 GrandSon-Son1-Parent-Son2。

3.2 python 2.2版本之后新式类

(python2有经典类,新式类两种类,python只3有新式类)采用的是C3算法,C3算法的具体过程是这样的
https://blog.csdn.net/u011467553/article/details/81437780

3.3 一个举例

在这里插入图片描述
计算过程

mro(A)	=mro(A(B,C))
		=[A]+merge(mro(B),mro(C),[B,C])
		=[A]+merge([B,D,E,O],[C,E,F,O],[B,C])
		=[A]+[B]+merge([D,E,O],[C,E,F,O],[C])
		#继续判断第一个列表里的表头D
		=[A]+[B,D]+merge([E,O],[C,E,F,O],[C])
		#E出来失败,开始判断第二个列表里的表头C
		=[A]+[B,D,C]+merge([E,O],[E,F,O])
		=[A]+[B,D,C,E]+merge([O],[F,O])
		=[A,B,D,C,E,F,O]

mro(B)	=mro(B(D,E))
		=[B]+merge(mro(D),mro(E),[D,E])
		=[B]+merge([D,O],[E,O],[D,E])
		=[B]+[D,E,O]
		=[B,D,E,O]
		
mro(C)	=mro(C(E,F))
		=[C]+merge(mro(E),mro(F),[E,F])
		=[C]+merge([E,O],[F,O],[E,F])
		=[C]+[E,F,O]
		=[C,E,F,O]

mro(D)	=mro(D(O))
		=[D]+merge(mro(O))
		=[D,O]

mro(E)	=mro(E(O))
		=[E]+merge(mro(O))
		=[E,O]
		
mro(F)	=mro(F(O))
		=[F]+merge(mro(O))
		=[F,O]

3.4 mro的用处

  1. 在构造函数里用super()调用init方法会按照mro的顺序依次调用mro列表的后一个init方法。
  2. 如果多个父类或者间接父类都定义了一个方法fun,我这个类直接调用fun方法选择的就是按照mro列表查找到的类的第一个fun方法。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值