Python的程序结构[2] -> 类/Class[4] -> 内建类 super
内建类 super / Built-in Type super
对于 super 可以从官方文档中看到基本介绍,super 接收一个类,以及类或类的实例,最终返回一个代理对象的实例。而 MRO 搜索也将被用在 super(同 getattr)上,因此 super 调用的不一定是父类的方法,而有可能是 sibling 的方法。还有一点值得注意的是,当传入的第二个参数被省略时,则返回的 super 对象未绑定,若有第二个参数,则第二个参数必须是第一个参数的实例或子类。官方文档对于 super 的定义如下,
super([type[, object-or-type]])
Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.
The __mro__ attribute of the type lists the method resolution search order used by both getattr() and super(). The attribute is dynamic and can change whenever the inheritance hierarchy is updated.
If the second argument is omitted, the super object returned is unbound. If the second argument is an object, isinstance(obj, type) must be true. If the second argument is a type, issubclass(type2, type) must be true (this is useful for classmethods).
下面给出super的使用例子。
Super的使用
首先定义一个基类 A 以及 show 方法,然后定义一个 B 继承自 A,在初始化中,使用 super 获得一个代理并调用初始化函数。
而当需要从 B 的 show 方法中调用 A 的 show 方法时,同样可以使用 super 来完成,同时 super 还具有两种写法,其功能都相同。
1 class A: 2 def __init__(self): 3 pass 4 5 def show(self): 6 print("This is A.") 7 8 class B(A): 9 def __init__(self): 10 super(B, self).__init__() 11 print(super(B, self)) 12 13 def show(self): 14 print("This is B.") 15 super(B, self).show() 16 super().show() 17 18 b = B() 19 b.show()
从输出的结果中可以看到,虽然在 B 中重载了 show 方法,但依旧可以通过 super 来调用这个被子类覆盖的方法。
<super: <class 'B'>, <B object>>
This is B.
This is A.
This is A.
由于 super 返回的是一个代理类,也就是说可以将其利用类属性保存下来使用,虽然这么做有些奇怪,但是却是可行的。
1 class C(A): 2 def __init__(self): 3 self.a = super(C, self) 4 print(self.a) 5 6 def show(self): 7 print("This is C.") 8 self.a.show() 9 10 c = C() 11 c.show()
最终显示结果如下,与每次 super 调用相同。
<super: <class 'C'>, <C object>> This is C. This is A.
最值得注意的一点在于,在 super 的使用过程中,不涉及任何关于父类 A 的传入,而当 A 被修改后,相应的 super 代理也会变化。这样在替换基类 A 为其他基类的时候,只需要做出很小的修改。
Super的MRO & 传参
下面的例子显示了带参数的方法使用 super 进行调用的使用方式,以及一个 MRO 搜索顺序的验证。
首先定义一个菱形继承 ABCD,此时最值得注意的是 B 类的 super 调用,此时 B 的基类是 A,但是 B 的 super 调用的却是类 C 的初始化函数,这是由于 MRO 的搜索规则所决定的。
1 class A: 2 def __init__(self, name): 3 print("A init.") 4 self.name = name 5 6 class B(A): 7 def __init__(self, age): 8 print("B init.") 9 self.age = age 10 super(B, self).__init__("LIKE") # This super() will call C.__init__() 11 12 class C(A): 13 def __init__(self, age): 14 print("C init.") 15 self.age = age 16 super(C, self).__init__("like") 17 18 class D(B, C): 19 def __init__(self): 20 print("D init.") 21 super(D, self).__init__(7) 22 23 d = D() 24 print(d.__dict__)
从输出的结果中可以看到,super 的调用符合 MRO,最终 d 的属性也可以验证,B 类初始化中的 super 调用的是 C 的初始化函数,因此 super 并不能简单的理解为调用父类方法。
D init. B init. C init. A init. {'age': 'LIKE', 'name': 'like'}
相关阅读
1. 关于内建类
2. MRO 搜索