马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 Stubborn 于 2020-2-22 04:10 编辑
super() 函数是用于调用父类(超类)的一个方法。super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
super()官方介绍:V3.8
官方介绍就不抄过来了,很多链接,容易被禁
个人理解:
将方法调用委托给 *type* 的父类或兄弟类,这个暂时不说,先了解,它怎么取找到,确认调用父类的方法。官方文档说,可以使用*object-or-type*的 [`__mro__`]属性,来确定它的搜索顺序。
class A: pass
class B: pass
class C(A, B): pass
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
class D:pass
class E(C, D):pass
print(E.mro())
# [<class '.E'>, <class '.C'>, <class '.A'>, <class '.B'>, <class '.D'>, <class 'object'>]
结合代码<*E→C→A→B→D*>可以很容易的看出,先检查自身,而后从继承的父类或者兄弟类(A和B),从左到右检查,父类如果还存在继承,优先被检查。
**一个类的 [`__mro__`](https://docs.python.org/zh-cn/3. ... .html#class.__mro__) 属性的三个准则:**
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
委托给父类可能都用过,那么委托兄弟类是什么意思呢?class A:
def spam(self):
print("A.spam")
super().spam()
class B:
def spam(self):
print("B.spam")
class C(A, B):
def spam(self):
print("C.sapm")
super().spam()
"""
C.sapm
A.spam
B.spam
"""
如代码示例,A类并没有父类,但是在继承结构中,B类和A类互为兄弟类。所以`super()`在A中调用的时候,最终却调用其兄弟的同名方法,这就是之前说的,*`super`函数返回一个委托类`type`的父类或者兄弟类方法调用的代理对象*。
注意
注意上面说的搜索顺序,如上C的mro顺序是[C→A→B→object],type为A的时候,则 `super()` 将会搜索 `B→object`,也就是说,同为兄弟类,在B中使用`super().spam()`,那么`super()`只会搜索`object`。可能说的有点绕口,说简白点,在如上的继承结构中,A可以委派给B兄弟类,但是B不能委派给A兄弟类,因为在mro中,super已经搜索不到spam()方法。
然后说下,为什么A可以委派给兄弟B类呢?
`super()`是`super(type, obj)`的简写,在调用`super()`时,`type`参数传入的是当前的类,而`obj`参数则是默认传入当前的实例对象,在`super()`的后续调用中,`obj`一直未变,而实际传入的`class`是动态变化,不过,在首次调用时,MRO就已经被确定,是`obj`所属类(即C)的MRO,因此`class`参数的作用就是从已确定的MRO中找到位于其后紧邻的类,作为再次调用`super()`时查找该方法的下一个类。
即,`super`函数这一部分的核心逻辑应该为
def super(class, obj):
mro_list = obj.__class__.mro()
next_parent_class = mro_list[mro_list.index(class)+1]
return next_parent_class
这就是为什么必须保证`isinstance(obj, type)`为`True`的原因,如果不是,那么可能`type`就不存在于`obj.__class__`的MRO列表中,该算法就无法正确找到下一个应当被查找的类。
因此,如果我们在某个类的父类中按照其MRO顺序,每个父类都写一个同名方法,同时每个该方法中都继续调用`super()`,直到在MRO列表`object`之前的最后一个类的同名方法中不再调用`super()`,那么在调用该方法时,会在各个父类中按照MRO列表的顺序依次被调用,这个过程中存在数据的传递,代表它们之间可以**共享**某些数据,这就实现了多继承协同工作。
而这种工作方式,通过重写方法是根本无法实现的。
案例:
我们试图达到如下目的:
一个类`Final`继承`Header`以获得属性`header`
同时我们通过混合其他类来快捷地修饰`header`属性,例如继承类`CookieMinix`为`header`添加`cookie`,而继承`UserAgentMinix`为`header`添加`user-agent`。注意,因为这些操作并不冲突,这些行为都不该相互覆盖。
class CookieMinix:
def get_header(self):
print('Add cookie')
ctx = super().get_header()
ctx["cookie"] = "cookies"
return ctx
class UserAgentMinix:
def get_header(self):
print('add user-agent')
ctx = super().get_header()
ctx["User-Agent"] = "浏览器头"
return ctx
class Header:
headers = {}
def get_header(self):
print('create headers dict')
return self.headers if self.headers else {}
class Final(CookieMinix, UserAgentMinix, Header):
def get_header(self):
return super().get_header()
header = Final().get_header()
当然,我们可以定义更多的混合类,并从中选取所需的类来快速得到想要的`header`属性, 在这个例子中,这两个混合类已经足够说明问题。我们实现了多继承协同工作的目标,通过混合不同个类,来模块化地快速得到想要的`header`属性。
而这种工作方法,通过单纯的重写某个方法根本无法实现的,因为重写任何方法,它会在MRO列表中找到最优先(也就是最靠前)的拥有同名方法的类,然后调用该方法,并且终止检索,某项属性仅仅会被**一个**方法所影响。
|