我们都知道在python继承中,子类调用父类的方法是使用super()函数,在2.X里格式是super(myclass,self).method(),在3.X里格式简化为super().method(),一般我们理解这个super()指代的就是父类,但有些时候情况并不是这样简单~
先看一个例子:
class D:
def out(self):
print('DDD')
class C(D):
def out(self):
print('CCC')
super().out()
class B(D):
def out(self):
print('BBB')
super().out()
class A(B,C):
def out(self):
print('AAA')
super().out()
a1=A()
a1.out()
输出:
AAA
BBB
CCC
DDD
我们看到竟然执行了类C的方法out,可以看到C的out方法是在B的out方法里调用的,也就是说B的out方法里的super()并不是B的父类D,而是B的兄弟类C!要解释这个现象我们先看一下类A的MRO(Method Resolution Order),即方法解析顺序。
print ([x.__name__ for x in A.__mro__])
输出:
['A', 'B', 'C', 'D', 'object']
用图来表示这四个类的关系就是:
object
|
D
/ \
B C
\ /
A
python3查找方法是用的是广度优先的策略,同一层级从左到右,所以整个查找顺序就如上面mro输出的那样,A>B>C>D>object,object默认是所有类的父类。B类out方法里的super()指代的是整个mro链中的兄弟类C,而不是他的父类D,所以从这里来看,super()不能简单的理解为父类,更准确的理解应该是mro链里的“父类”。
为了验证这个的观点,我们直接调用B的out函数来看看:
b1=B()
b1.out()
输出:
BBB
DDD
#查看B的mro:
print ( [x.__name__ for x in B.__mro__])
输出:['B', 'D', 'object']
可以看到这里没有执行C类的out函数了,原因就是类B的out函数里的super()指代的是mro链里的“父类”D了
从上面可以看到python的多继承使用还是要小心谨慎,不然就会出现“儿子找错爸爸”的情况,
这种用法在django里的class-based view里很常见也很有用。
比如我们希望当用户登录后在网站任何一个页面都要显示他的未读消息数量,这个重复的逻辑如果写在每个视图函数或类中就显得特别冗余,那么我们就可以创建一个mixin专门来返回所有页面共用的数据:
class BaseMixin(object):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(**kwargs)
context['unread_info']=info.objects.filter(read=0)
return context
代码第三行调用了super()函数,但是如果把super当成父类来理解的话就会有问题,因为BaseMixin的父类是基类object,object类是不可能有get_context_data方法的,所以这里还是要理解为mro链里的“父类”。
具体使用时把mixin放在前面,如:
class Index(BaseMixin,ListView):
pass
这样,Index就自动包含了unread_info的数据,而get_context_data这个函数,就会到其兄弟类ListView里查找了