super()如何处理多重继承?例如,给定:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
Third的哪个父方法执行super()。__init__ refer to?我可以选择哪些运行吗?
我知道这与方法解析顺序(MRO)有关。
考虑从子类调用super(). foo()。方法解析顺序(MRO)方法是解析方法调用的顺序。
案例1:单继承
在这种情况下,super(). foo()将在层次结构中向上搜索,并将考虑最接近的实现,如果找到,否则引发异常。在任何被访问的子类和它在层次结构上的超类之间,“is a”关系将始终为True。但是这个故事在多重继承中并不总是一样的。
案例2:多重继承
在这里,当搜索super(). foo()实现时,层次结构中每个被访问的类都可能是一个关系,也可能不是。考虑以下例子:
class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass
这里,I是层次结构中最低的类。层次图和MRO为我将
(红色数字为MRO)
MRO是I E C D A H F G B对象
注意,类X只有在继承自它的所有子类都被访问过的情况下才会被访问。例如,如果一个类下面有一个箭头,而你还没有访问过这个箭头,那么你永远不应该访问这个类)。
在这里,注意在访问类C之后,D被访问,尽管C和D DO NOT have是它们之间的关系(但它们都与a有关系)。这是super()不同于单一继承的地方。
考虑一个稍微复杂一点的例子:
(红色数字为MRO)
MRO是I E C H D A F G B对象
在这种情况下,我们从I到E再到c。下一步是A,但我们还没有访问A的子类D。然而,我们不能访问D,因为我们还没有访问D的子类H。剩下H作为下一个要访问的类。记住,如果可能的话,我们试图在层次结构中向上,所以我们访问它最左边的超类D。在D之后,我们访问A,但我们不能向上到object,因为我们还没有访问F、G和b。这些类,依次,为I完成MRO。
注意,任何类都不能在MRO中出现超过一次。
这就是super()在继承层次结构中的查找方式。
资源来源:Richard L Halterman Python编程基础
也许还可以添加一些东西,比如Django rest_framework和装饰器的一个小例子。这为隐含的问题提供了答案:“我为什么想要这个?”
如前所述:我们使用Django rest_framework,我们使用泛型视图,对于数据库中的每种类型的对象,我们发现我们有一个视图类为对象列表提供GET和POST,另一个视图类为单个对象提供GET、PUT和DELETE。
现在我们要用Django的login_required来装饰POST、PUT和DELETE。注意,这涉及到两个类,而不是两个类中的所有方法。
解决方案可以通过多重继承。
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class LoginToPost:
@method_decorator(login_required)
def post(self, arg, *args, **kwargs):
super().post(arg, *args, **kwargs)
其他方法也是如此。
在我的具体类的继承列表,我将添加我的LoginToPost之前ListCreateAPIView和LoginToPutOrDelete之前RetrieveUpdateDestroyAPIView。我的具体类的get将保持未修饰。
在这种情况下,你试图继承的每个类都有自己的init位置参数,只需调用每个类自己的init方法,如果试图继承多个对象,则不要使用super。
class A():
def __init__(self, x):
self.x = x
class B():
def __init__(self, y, z):
self.y = y
self.z = z
class C(A, B):
def __init__(self, x, y, z):
A.__init__(self, x)
B.__init__(self, y, z)
>>> c = C(1,2,3)
>>>c.x, c.y, c.z
(1, 2, 3)
把这个答案贴出来供我将来参考。
Python多重继承应该使用菱形模型,并且函数签名不应该在模型中更改。
A
/ \
B C
\ /
D
示例代码片段为;-
class A:
def __init__(self, name=None):
# this is the head of the diamond, no need to call super() here
self.name = name
class B(A):
def __init__(self, param1='hello', **kwargs):
super().__init__(**kwargs)
self.param1 = param1
class C(A):
def __init__(self, param2='bye', **kwargs):
super().__init__(**kwargs)
self.param2 = param2
class D(B, C):
def __init__(self, works='fine', **kwargs):
super().__init__(**kwargs)
print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")
d = D(name='Testing')
这里类A是对象
考虑从子类调用super(). foo()。方法解析顺序(MRO)方法是解析方法调用的顺序。
案例1:单继承
在这种情况下,super(). foo()将在层次结构中向上搜索,并将考虑最接近的实现,如果找到,否则引发异常。在任何被访问的子类和它在层次结构上的超类之间,“is a”关系将始终为True。但是这个故事在多重继承中并不总是一样的。
案例2:多重继承
在这里,当搜索super(). foo()实现时,层次结构中每个被访问的类都可能是一个关系,也可能不是。考虑以下例子:
class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass
这里,I是层次结构中最低的类。层次图和MRO为我将
(红色数字为MRO)
MRO是I E C D A H F G B对象
注意,类X只有在继承自它的所有子类都被访问过的情况下才会被访问。例如,如果一个类下面有一个箭头,而你还没有访问过这个箭头,那么你永远不应该访问这个类)。
在这里,注意在访问类C之后,D被访问,尽管C和D DO NOT have是它们之间的关系(但它们都与a有关系)。这是super()不同于单一继承的地方。
考虑一个稍微复杂一点的例子:
(红色数字为MRO)
MRO是I E C H D A F G B对象
在这种情况下,我们从I到E再到c。下一步是A,但我们还没有访问A的子类D。然而,我们不能访问D,因为我们还没有访问D的子类H。剩下H作为下一个要访问的类。记住,如果可能的话,我们试图在层次结构中向上,所以我们访问它最左边的超类D。在D之后,我们访问A,但我们不能向上到object,因为我们还没有访问F、G和b。这些类,依次,为I完成MRO。
注意,任何类都不能在MRO中出现超过一次。
这就是super()在继承层次结构中的查找方式。
资源来源:Richard L Halterman Python编程基础
考虑子AB,父A和B在它们的构造函数中有关键字参数。
A B
\ /
AB
要初始化AB,需要显式调用父类构造函数,而不是使用super()。
例子:
class A():
def __init__(self, a="a"):
self.a = a
print(f"a={a}")
def A_method(self):
print(f"A_method: {self.a}")
class B():
def __init__(self, b="b"):
self.b = b
print(f"b={b}")
def B_method(self):
print(f"B_method: {self.b}")
def magical_AB_method(self):
print(f"magical_AB_method: {self.a}, {self.b}")
class AB(A,B):
def __init__(self, a="A", b="B"):
# super().__init__(a=a, b=b) # fails!
A.__init__(self, a=a)
B.__init__(self, b=b)
self.A_method()
self.B_method()
self.magical_AB_method()
A()
>>> a=a
B()
>>> b=b
AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B
为了演示两个父类被组合到子类中,请考虑在类B中定义的magical_AB_method。当从B的实例调用时,该方法失败,因为它不能访问A中的成员变量。然而,当从子类AB的实例调用时,该方法工作,因为它从A继承了所需的成员变量。
B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'
AB().magical_AB_method()
>>> magical_AB_method: A, B