super()干了啥

super()继承

转自:https://www.cnblogs.com/silencestorm/p/8404046.html

class Base(object):
    def __init__(self):
        print("Enter Base")
        print("Leave Base")

class A(Base):
    def __init__(self):
        print('Enter A')
        super(A, self).__init__()  # 等同于python3: super().__init__()
        print('Leave A')

class B(Base):
    def __init__(self):
        print('Enter B')
        super(B, self).__init__()
        print('Leave B')

class C(A,B):
    def __init__(self):
        print('Enter C')
        super(C, self).__init__()
        print('Leave C')

C()
print(C.mro())

"""
Enter C
Enter A
Enter B
Enter Base
Leave Base
Leave B
Leave A
Leave C
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
"""

每一个类,都有一个 MRO(Method Resolution Order) 列表。这是python 在多继承时,搜索属性和方法时的顺序。可以通过 class.mro()obj.__class__.mro() 来查看。

>>> C.mro()
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]

super() 的原理:

def super(class, instance):
    mro = instance.__class__.mro()  # 获取 self 的类的 mro 列表
    return mro[mro.index(class)+1]  # 返回显式指定的类 class 的下一个父类

所以上面代码执行顺序:

line 24: 实例化 C 类
line 21: 查找 self 实例的 mro 中位于 C 类之后的那个类,即 A 类,找到后执行它的 __init__ 方法(注意此时类 A 的 __init__ 方法已经绑定给了 self 实例,即 C 类的实例。换句话说,self 实例会作为参数传递给 A.__init__ 方法)。
line 9:  此时 super(A, self).__init__() 中的 self,代表的是 C类实例,所以下一步查找到 C 类 mro 中的类A后面的是类 B,执行 B.__init(self) 方法
line 15: 此时的 self 还是 C 实例,所以根据mro顺序,下一步执行mro中的 Base类

简而言之,super(A, self) 可以按照 mro 顺序,找到类 A 后面的类,然后将那个类的某个方法,绑定给 self 实例,可以让实例调用父类的方法。

三种继承父类方法的写法:

类名.方法名(self, 参数)  # 直接调用类 A 的某个方法,此时要将 self 作为参数传递给该方法

super().方法名(参数)  # 自动查找当前类的第一个父类的某个方法

super(A, b).方法名(参数)  # 自动查找当前实例 b 的类的 MRO 列表中类 A 之后的那个类,并调用其方法

三种方法的区别:

  • 类名.方法名(self, 参数) 这种方法是最原始的方法。它可以将当前 self 实例和类的方法绑定起来,然后调用该方法,因此需要手动将 self 实例作为参数传递给方法
  • super().方法名(参数) 更推荐的写法,它会自动从当前实例所属类的 mro 中查找下一个父类,并调用该父类的方法,当你调用 super().方法名 时,super() 会自动关联到当前类和当前实例(即 self),并确保方法调用时正确传递 self。因此不需要手动传递 self 参数给方法,super 默认已经给方法传递了 self。 其实相当于调用了 父类.方法(self, 参数)
  • super(A, obj).方法名(参数) 它是较老的写法,同时也是更灵活的写法:它可以指定从 obj 的类的 MRO 列表中,查找类 A 之后的那个类,并调用该类的方法,也就是它可以跳过 MRO 列表中的某些类,从而指定使用某个父类的方法。它也不需要手动传递 self 参数给方法

强调一点:super() 可以调用父类的任意方法,而不仅仅是 __init__ 这种特殊方法。

其他事项:对于 __new__(cls, *args, **kwargs) 方法,其中的参数 cls 代表的是一个类,实例化一个类时,不需要手动传递这个参数,但是如果继承父类的此方法,就需要手动传递,因为需要让父类知道给哪个类创建实例化对象,比如:

class Base(object):
   def __new__(cls, *args, **kwargs):
       if not hasattr(cls, '_instance'):
           cls._instance = super(Base, cls).__new__(cls)  # 需要把 cls 作为参数传递给父类的 __new__ 方法
       return cls._instance
class Base(object):
    def __init__(self, name):
        print("Enter Base")
        super().__init__()  # 第一种:直接继承使用当前类之后的父类的 __init__() 方法,这里是 object.__init__() 方法
        print("Leave Base")

class A(Base):
    def __init__(self, name):
        print('Enter A')
        super(Base, self).__init__()  # 第二种:指定从 Base 之后查找父类,并调用该父类的方法(这种方式可以跳过 MRO 列表中的某个父类,这里跳过了父类 Base,而是使用 Base 之后的类 object 的 __init__ 方法)
        print('Leave A')

class B(A):
    def __init__(self, name):
        Base.__init__(self, name)  # 第三种: 直接指定使用某个类的方法(这种用法,必须要将 self 作为参数传递到方法中,目的是将 self 实例和该方法绑定)
        super(A, self).__init__(name)  # 等效于上一行:这里跳过了父类 A,下一个父类刚好是 Base
        self.name = name


b = B("haha")


posted @ 2021-08-09 13:58  wztshine  阅读(80)  评论(0)    收藏  举报