一、super
1、概念
super:是一个内置的代理类对象而不是一个函数对象,
作用:运行super()会得到一个代理实例对象,即一个委托类的父类或者兄弟类方法调用的代理对象。
2、原型
super([type1][, object-or-type2])
3、参数说明
type1:指委托类
object-or-type2:当该参数为object时必须满足isinstance(object, type1) == True:当该参数为type2时,必须满足issubclass(type2, type1) == True。
使用super时可以直接省略掉参数写为super(),因为解释器在执行时会自动添加相应的参数。
4、原理
目前能力有限,还写不出一个功能类似super()的代理类。
此处只是为了用python代码说明super内部实现的逻辑,并不是真实的super相关代码
def super(type1, obj_type2):
obj_type2 = obj_type2
if isinstance(obj_type2, type1):
mro_list = obj_type2.__class__.mro()
elif issubclass(obj_type2, type1):
mro_list = obj_type2.mro()
else:
print("参数有误!!!")
raise Exception
next_class = mro_list[mro_list.index(type1) + 1]
return next_class
在首次调用时,MRO就已经被确定,是object所属类(即C)的MRO或type2所对应的MRO,因此type1参数的作用就是从已确定的MRO中找到位于其后紧邻的类,作为再次调用super()时查找该方法的下一个类。
5、要点:
- super()使用的时候需要传递两个参数,在类中可以省略不写,我们使用super()来找父类或者兄弟类的方法;
- super()是根据第二个参数来计算MRO,根据顺序查找第一个参数类后的方法。
- super()第二个参数是类,得到的方法是函数,使用时要传self参数。第二个参数是对象,得到的是绑定方法,不需要再传self参数。
给使用super()的一些建议:
- super()调用的方法要存在;
- 传递参数的时候,尽量使用*args 与**kwargs;
- 父类中的一些特性,比如【】、重写了__getattr__,super对象是不能使用的。
- super()第二个参数传的是类的时候,建议调用父类的类方法和静态方法。
二、多继承
1、多继承
Python虽然语法上支持多继承,但是却不推荐使用多继承,而是推荐使用单继承,这样可以保证编程思路更清晰,也可以避免不必要的麻烦。
当以一个子类有多个直接父类时,该子类会继承得到所有父类的方法,但是如果其中有多个父类包含同名方法会发生什么?此时排在前面的父类中的方法会“遮蔽”后面父类中的方法。
示例:
class Base:
def __new__(cls, base):
print(cls)
print("Base.__new__")
return super(Base, cls).__new__(cls)
def __init__(self, base):
print("Base.__init__")
self.base = base
print(self.base)
class A(Base):
def __new__(cls, base, a, b):
"""
__new__此处不仅接收参数base,a, 还须接受参数b因为
C.__mro__ = (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
python3支持多继承,搜索父类及兄弟类时是按照广度优先搜索遍历,遍历结果也就是C.__mro__从左到右的顺序,
此处下一个时A的兄弟类B
"""
print(cls)
print("A.__new__")
return super(A, cls).__new__(cls, base, b)
def __init__(self, base, a, b):
super(A, self).__init__(base, b)
print("A.__init__")
self.a = a
print(self.a)
class B(Base):
def __new__(cls, base, b):
print(cls)
print("B.__new__")
return super(B, cls).__new__(cls, base)
def __init__(self, base, b):
super(B, self).__init__(base)
print("B.__init__")
self.b = b
print(self.b)
class C(A, B):
__c_class__ = 4
def __new__(cls, base, a, b, c):
print(cls)
print("C.__new__")
return super(C, cls).__new__(cls, base, a, b)
def __init__(self, base, a, b, c):
super(C, self).__init__(base, a, b)
print("C.__init__")
self.c = c
print(self.c)
print(C.__mro__)
test = C(base=0, a=1, b=2, c=3)
print(test.base, test.a, test.b, test.c)
"""
运行结果:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
<class '__main__.C'>
C.__new__
<class '__main__.C'>
A.__new__
<class '__main__.C'>
B.__new__
<class '__main__.C'>
Base.__new__
Base.__init__
0
B.__init__
2
A.__init__
1
C.__init__
3
0 1 2 3
Process finished with exit code 0
"""
2、多继承协同
示例:
class Minix1:
"""该混合类为header列表末尾添加data1"""
def get_header(self):
print('run Minix1.get_header')
ctx = super().get_header()
ctx.append('data1')
return ctx
class Minix2:
"""该混合类为header列表头部添加data2"""
def get_header(self):
print('run Minix2.get_header')
ctx = super().get_header()
ctx.insert(0, 'data2')
return ctx
class Header:
header = []
def get_header(self):
print('run Headers.get_header')
return self.header if self.header else []
class Final(Minix1, Minix2, Header):
def get_header(self):
return super().get_header()
print(Final.mro())
header = Final().get_header()
print(header)
"""
[<class '__main__.Final'>, <class '__main__.Minix1'>, <class '__main__.Minix2'>, <class '__main__.Header'>, <class 'object'>]
run Minix1.get_header
run Minix2.get_header
run Headers.get_header
['data2', 'data1']
Process finished with exit code 0
"""
看来,运行得很成功,我们实现了多继承协同工作的目标,通过混合不同个类,来模块化地快速得到想要的header属性。而这种工作方法,通过单纯的重写某个方法根本无法实现的,因为重写任何方法,它会在MRO列表中找到最优先(也就是最靠前)的拥有同名方法的类,然后调用该方法,并且终止检索,某项属性仅仅会被一个方法所影响。
这个特性,在Django的CBV中有相当程度的应用。