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"
super().__init__
指的是 Third
的哪个父方法?我可以选择哪些运行?
我知道这与方法解析顺序(MRO)有关。
super()
的情况。我不建议将它与使用线性继承的类一起使用,这只是无用的开销。
super()
的另一个问题是,它强制每个子类也使用它,而当不使用 super()
时,每个子类都可以自己决定。如果使用它的开发人员不知道 super()
或不知道它已被使用,则可能会出现难以追踪的 mro 问题。
super
使您的类可 用于多重继承,无论它是否使用多重继承。 (但您的第二点是有效的; super
的使用确实是您的类的公共接口的一部分,而不仅仅是实现细节。)
Guido 本人在他的博客文章 Method Resolution Order 中详细介绍了这一点(包括之前的两次尝试)。
在您的示例中,Third()
将调用 First.__init__
。 Python 在类的父类中查找每个属性,因为它们从左到右列出。在这种情况下,我们正在寻找 __init__
。所以,如果你定义
class Third(First, Second):
...
Python 将从查看 First
开始,如果 First
没有该属性,那么它将查看 Second
。
当继承开始跨越路径时(例如,如果 First
从 Second
继承),这种情况会变得更加复杂。阅读上面的链接以获取更多详细信息,但简而言之,Python 将尝试维护每个类出现在继承列表中的顺序,从子类本身开始。
因此,例如,如果您有:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
MRO 将是 [Fourth, Second, Third, First].
顺便说一句:如果 Python 找不到一致的方法解析顺序,它将引发异常,而不是退回到可能让用户感到惊讶的行为。
模棱两可的 MRO 示例:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
Third
的 MRO 应该是 [First, Second]
还是 [Second, First]
?没有明显的期望,Python 会报错:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
为什么上面的示例缺少 super()
调用?这些示例的重点是展示如何构建 MRO。它们不用于打印 "first\nsecond\third"
或其他内容。您可以(当然也应该)试用该示例,添加 super()
调用,看看会发生什么,并更深入地了解 Python 的继承模型。但我的目标是保持简单并展示 MRO 是如何构建的。它是按照我解释的那样构建的:
>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)
您的代码和其他答案都是错误的。他们缺少合作子类化工作所需的前两个类中的 super()
调用。更好的是:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
输出:
>>> Third()
second
first
third
super()
调用会在每个步骤中找到 MRO 中的下一个方法,这就是为什么 First
和 Second
也必须拥有它,否则执行会在 Second.__init__()
结束时停止。
没有 First
和 Second
中的 super()
调用,输出缺少 second
:
>>> Third()
first
third
super
要么无法运行(因为参数不匹配),或者它不会调用几个碱基(因为您没有在其中一个断开链接的碱基中写 super
)!
我想详细说明 the answer by lifeless,因为当我开始阅读有关如何在 Python 的多重继承层次结构中使用 super() 时,我并没有立即明白。
您需要了解的是,super(MyClass, self).__init__()
根据所使用的方法解析排序 (MRO) 算法在完整继承层次结构的上下文中提供 next __init__
方法 .
最后一部分对于理解至关重要。让我们再次考虑这个例子:
#!/usr/bin/env python2
class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"
class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"
class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"
According to this article about Method Resolution Order 由 Guido van Rossum 提出,解析 __init__
的顺序是使用“深度优先从左到右遍历”计算的(在 Python 2.3 之前):
Third --> First --> object --> Second --> object
删除所有重复项后,除了最后一个,我们得到:
Third --> First --> Second --> object
因此,让我们看看当我们实例化 Third
类的实例时会发生什么,例如 x = Third()
。
根据 MRO Third.__init__ 执行。打印 Third():进入然后 super(Third, self).__init__() 执行,MRO 返回 First.__init__ 被调用。首先.__init__ 执行。打印 First(): 输入然后 super(First, self).__init__() 执行并且 MRO 返回被调用的 Second.__init__。第二个.__init__ 执行。打印 Second(): 输入然后 super(Second, self).__init__() 执行并且 MRO 返回被调用的 object.__init__。 object.__init__ 执行(代码中没有打印语句)执行返回到 Second.__init__,然后打印 Second():退出执行返回到 First.__init__,然后打印 First():退出执行返回到 Third.__init__然后打印 Third():exiting
这详细说明了为什么实例化 Third() 会导致:
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
MRO 算法已经从 Python 2.3 开始改进,可以在复杂的情况下很好地工作,但我想在大多数情况下使用“深度优先从左到右遍历”+“删除重复期望最后一个”仍然有效(请如果不是这种情况,请发表评论)。请务必阅读 Guido 的博客文章!
Third
没有从 Second
继承,则 super(First, self).__init__
将调用 object.__init__
并在返回后,将打印“first”。但是因为 Third
继承自 First
和 Second
,而不是在 First.__init__
之后调用 object.__init__
,MRO 规定只保留对 object.__init__
的最终调用,并且 First
中的打印语句和Second
直到 object.__init__
返回。由于 Second
是最后一个调用 object.__init__
,它在 Second
中返回,然后在 First
中返回。
subclass
是子类,它会将 List[subclass]
识别为 List[superclass]
superclass
(List
来自 PEP 483 iirc 的 typing
模块)。
这被称为Diamond Problem,该页面在 Python 上有一个条目,但简而言之,Python 会从左到右调用超类的方法。
object
,但这是所有类的公共基类,在 this i> 问题)。 Python 调用方法的确切顺序不那么简单,C3 linearisation of the class hierarchy 可能导致非常不同的顺序。
我知道这并不能直接回答 super()
问题,但我觉得它足够相关,可以分享。
还有一种方法可以直接调用每个继承的类:
class First(object):
def __init__(self):
print '1'
class Second(object):
def __init__(self):
print '2'
class Third(First, Second):
def __init__(self):
Second.__init__(self)
请注意,如果您这样做,您将不得不手动调用每个,因为我很确定 First
的 __init__()
不会被调用。
First
和 Second
都继承另一个类并直接调用它,那么这个公共类(菱形的起点)会被调用两次。 super 正在避免这种情况。
object
被调用两次的好点。我没想到。我只是想说明您直接调用父类。
全面的
假设所有内容都来自 object
(如果不是,则您只能靠自己),Python 会根据您的类继承树计算方法解析顺序 (MRO)。 MRO 满足 3 个特性:
一个班级的孩子在他们的父母之前
左父母先于右父母
一个类在 MRO 中只出现一次
如果不存在这样的排序,Python 会出错。其内部工作是类祖先的 C3 线性化。在此处阅读所有相关信息:https://www.python.org/download/releases/2.3/mro/
因此,在下面的两个示例中,它是:
孩子 左 右 父母
调用方法时,该方法在 MRO 中的第一次出现就是被调用的方法。任何未实现该方法的类都将被跳过。在该方法中对 super
的任何调用都将在 MRO 中调用该方法的下一次出现。因此,在继承中放置类的顺序以及在方法中调用 super
的位置都很重要。
请注意,您可以使用 __mro__
方法在 python 中查看 MRO。 Child.__mro__
在以下任何示例中返回:
(__main__.Child, __main__.Left, __main__.Right, __main__.Parent, object)
例子
以下所有示例都具有类的菱形继承,如下所示:
Parent
/ \
/ \
Left Right
\ /
\ /
Child
在每种方法中都使用 super first
class Parent(object):
def __init__(self):
super(Parent, self).__init__()
print("parent")
class Left(Parent):
def __init__(self):
super(Left, self).__init__()
print("left")
class Right(Parent):
def __init__(self):
super(Right, self).__init__()
print("right")
class Child(Left, Right):
def __init__(self):
super(Child, self).__init__()
print("child")
Child()
输出:
parent
right
left
child
每种方法都有超级最后
class Parent(object):
def __init__(self):
print("parent")
super(Parent, self).__init__()
class Left(Parent):
def __init__(self):
print("left")
super(Left, self).__init__()
class Right(Parent):
def __init__(self):
print("right")
super(Right, self).__init__()
class Child(Left, Right):
def __init__(self):
print("child")
super(Child, self).__init__()
Child()
输出:
child
left
right
parent
当不是所有的类都调用 super
如果不是继承链调用 super
中的所有类,继承顺序最重要。例如,如果 Left
不调用 super,则永远不会调用 Right
和 Parent
上的方法:
class Parent(object):
def __init__(self):
print("parent")
super(Parent, self).__init__()
class Left(Parent):
def __init__(self):
print("left")
class Right(Parent):
def __init__(self):
print("right")
super(Right, self).__init__()
class Child(Left, Right):
def __init__(self):
print("child")
super(Child, self).__init__()
Child()
输出:
child
left
或者,如果 Right
不调用 super
,仍会跳过 Parent
:
class Parent(object):
def __init__(self):
print("parent")
super(Parent, self).__init__()
class Left(Parent):
def __init__(self):
print("left")
super(Left, self).__init__()
class Right(Parent):
def __init__(self):
print("right")
class Child(Left, Right):
def __init__(self):
print("child")
super(Child, self).__init__()
这里,Child()
输出:
child
left
right
在特定父对象上调用方法
如果要访问特定父类的方法,则应直接引用该类,而不是使用 super。 Super 是关于遵循继承链,而不是获取特定类的方法。
以下是如何引用特定父母的方法:
class Parent(object):
def __init__(self):
super(Parent, self).__init__()
print("parent")
class Left(Parent):
def __init__(self):
super(Left, self).__init__()
print("left")
class Right(Parent):
def __init__(self):
super(Right, self).__init__()
print("right")
class Child(Left, Right):
def __init__(self):
Parent.__init__(self)
print("child")
在这种情况下,Child()
输出:
parent
child
Child
中的 super()
访问 Left
。假设我想从 Child
内部访问 Right
。有没有办法使用 super 从 Child
访问 Right
?还是应该直接从 super
内部调用 Right
?
这就是我如何解决具有用于初始化的不同变量的多重继承以及具有具有相同函数调用的多个 MixIn 的问题。我必须显式地将变量添加到传递的 **kwargs 并添加一个 MixIn 接口作为超级调用的端点。
这里的 A
是一个可扩展的基类,而 B
和 C
都是提供函数 f
的 MixIn 类。 A
和 B
在其 __init__
中都需要参数 v
,而 C
需要 w
。函数 f
采用一个参数 y
。 Q
继承自所有三个类。 MixInF
是 B
和 C
的混合接口。
此代码的 IPython NoteBook
带有代码示例的 Github Repo
class A(object):
def __init__(self, v, *args, **kwargs):
print "A:init:v[{0}]".format(v)
kwargs['v']=v
super(A, self).__init__(*args, **kwargs)
self.v = v
class MixInF(object):
def __init__(self, *args, **kwargs):
print "IObject:init"
def f(self, y):
print "IObject:y[{0}]".format(y)
class B(MixInF):
def __init__(self, v, *args, **kwargs):
print "B:init:v[{0}]".format(v)
kwargs['v']=v
super(B, self).__init__(*args, **kwargs)
self.v = v
def f(self, y):
print "B:f:v[{0}]:y[{1}]".format(self.v, y)
super(B, self).f(y)
class C(MixInF):
def __init__(self, w, *args, **kwargs):
print "C:init:w[{0}]".format(w)
kwargs['w']=w
super(C, self).__init__(*args, **kwargs)
self.w = w
def f(self, y):
print "C:f:w[{0}]:y[{1}]".format(self.w, y)
super(C, self).f(y)
class Q(C,B,A):
def __init__(self, v, w):
super(Q, self).__init__(v=v, w=w)
def f(self, y):
print "Q:f:y[{0}]".format(y)
super(Q, self).f(y)
Q
对 w
和 v
的使用。 *args
和 **kwargs
专门用于超级调用。这段代码有一些微妙之处。避免多重继承;如果可以,请使用组合
关于 @calfzhou's comment,您可以像往常一样使用 **kwargs
:
class A(object):
def __init__(self, a, *args, **kwargs):
print("A", a)
class B(A):
def __init__(self, b, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
print("B", b)
class A1(A):
def __init__(self, a1, *args, **kwargs):
super(A1, self).__init__(*args, **kwargs)
print("A1", a1)
class B1(A1, B):
def __init__(self, b1, *args, **kwargs):
super(B1, self).__init__(*args, **kwargs)
print("B1", b1)
B1(a1=6, b1=5, b="hello", a=None)
结果:
A None
B hello
A1 6
B1 5
您也可以按位置使用它们:
B1(5, 6, b="hello", a=None)
但是您必须记住MRO,这确实令人困惑。您可以使用 keyword-only parameters 避免这种情况:
class A(object):
def __init__(self, *args, a, **kwargs):
print("A", a)
等等。
我可能有点烦人,但我注意到人们在重写方法时每次都忘记使用 *args
和 **kwargs
,而这是对这些“神奇变量”的少数真正有用和理智的使用之一。
__new__
并在其中调用 B.__new__()
,例如,在 __init__
中调用 B.__init__()
。但这是一个过于复杂的...
另一个尚未涉及的点是为类的初始化传递参数。由于 super
的目标取决于子类,因此传递参数的唯一好方法是将它们打包在一起。然后注意不要有不同含义的相同参数名称。
例子:
class A(object):
def __init__(self, **kwargs):
print('A.__init__')
super().__init__()
class B(A):
def __init__(self, **kwargs):
print('B.__init__ {}'.format(kwargs['x']))
super().__init__(**kwargs)
class C(A):
def __init__(self, **kwargs):
print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
super().__init__(**kwargs)
class D(B, C): # MRO=D, B, C, A
def __init__(self):
print('D.__init__')
super().__init__(a=1, b=2, x=3)
print(D.mro())
D()
给出:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__
直接调用超类 __init__
以更直接地分配参数很诱人,但如果超类中有任何 super
调用和/或更改 MRO 并且可能多次调用 A 类,则取决于实现.
总结:协作继承和用于初始化的超级和特定参数不能很好地协同工作。
考虑调用从子类调用的 super().Foo()
。 方法解析顺序 (MRO) 方法是解析方法调用的顺序。
案例一:单继承
在这种情况下,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
是层次结构中的最低类。 I
的层次结构图和 MRO 将是
https://i.stack.imgur.com/4FfzD.png
(红色数字显示 MRO)
MRO 是 I E C D A H F G B object
请注意,类 X
只有在其继承自它的所有子类都已被访问时才会被访问(即,您永远不应该访问具有箭头的类,该箭头来自您没有从下面的类进入尚未访问)。
在这里,请注意,在访问类 C
之后,虽然 C
和 D
之间没有 is 关系(但都与 A
有关系),但访问了 D
。这就是 super()
与单继承不同的地方。
考虑一个稍微复杂一点的例子:
https://i.stack.imgur.com/DBUO.png
(红色数字显示 MRO)
MRO 是 I E C H D A F G B object
在这种情况下,我们从 I
到 E
再到 C
。下一步是 A
,但我们还没有访问 D
,它是 A
的子类。但是,我们不能访问 D
,因为我们还没有访问 D
的子类 H
。叶子 H
作为下一个要访问的类。请记住,如果可能,我们会尝试在层次结构中上升,因此我们访问其最左侧的超类 D
。在 D
之后,我们访问了 A
,但我们无法提出反对,因为我们还没有访问 F
、G
和 B
。这些类按顺序完善了 I
的 MRO。
请注意,任何课程在 MRO 中都不能出现多次。
这就是 super() 在继承层次结构中的查找方式。
资源学分:Richard L Halterman Python 编程基础
在 python 3.5+ 中,继承看起来可以预测并且对我来说非常好。请看这段代码:
class Base(object):
def foo(self):
print(" Base(): entering")
print(" Base(): exiting")
class First(Base):
def foo(self):
print(" First(): entering Will call Second now")
super().foo()
print(" First(): exiting")
class Second(Base):
def foo(self):
print(" Second(): entering")
super().foo()
print(" Second(): exiting")
class Third(First, Second):
def foo(self):
print(" Third(): entering")
super().foo()
print(" Third(): exiting")
class Fourth(Third):
def foo(self):
print("Fourth(): entering")
super().foo()
print("Fourth(): exiting")
Fourth().foo()
print(Fourth.__mro__)
输出:
Fourth(): entering
Third(): entering
First(): entering Will call Second now
Second(): entering
Base(): entering
Base(): exiting
Second(): exiting
First(): exiting
Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)
如您所见,它以与继承顺序相同的顺序为每个继承的链准确调用一次 foo。您可以通过调用 .mro 获得该订单:
第四 -> 第三 -> 第一个 -> 第二个 -> 基础 -> 对象
如果您尝试继承的每个类都有自己的位置参数作为它的 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)
class First(object):
def __init__(self, a):
print "first", a
super(First, self).__init__(20)
class Second(object):
def __init__(self, a):
print "second", a
super(Second, self).__init__()
class Third(First, Second):
def __init__(self):
super(Third, self).__init__(10)
print "that's it"
t = Third()
输出是
first 10
second 20
that's it
对 Third() 的调用定位在 Third 中定义的 init。并在该例程中调用 super 调用 First 中定义的 init。 MRO=[第一,第二]。现在调用 First 中定义的 init 中的 super 将继续搜索 MRO 并找到 Second 中定义的 init,并且对 super 的任何调用都会命中默认对象 init。我希望这个例子能阐明这个概念。
如果您不从 First 调用 super 。链条停止,您将获得以下输出。
first 10
that's it
我想在顶部添加 what @Visionscaper says:
Third --> First --> object --> Second --> object
在这种情况下,解释器不会过滤掉对象类,因为它是重复的,而是因为 Second 出现在头部位置而不出现在层次结构子集中的尾部位置。而对象仅出现在尾部位置,在 C3 算法中不被视为确定优先级的强位置。
C 类 L(C) 的线性化 (mro) 是
C类
加上其父 P1, P2, .. = L(P1, P2, ...) 的线性化及其父 P1, P2, .. 的列表的合并
其父 P1, P2, .. = L(P1, P2, ...) 的线性化和
其父母 P1、P2、.. 的列表
线性化合并是通过选择作为列表头部而不是尾部出现的公共类来完成的,因为顺序很重要(将在下面变得清晰)
Third 的线性化可以计算如下:
L(O) := [O] // the linearization(mro) of O(object), because O has no parents
L(First) := [First] + merge(L(O), [O])
= [First] + merge([O], [O])
= [First, O]
// Similarly,
L(Second) := [Second, O]
L(Third) := [Third] + merge(L(First), L(Second), [First, Second])
= [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2,
= [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
= [Third, First, Second] + merge([O], [O])
= [Third, First, Second, O]
因此对于以下代码中的 super() 实现:
class First(object):
def __init__(self):
super(First, self).__init__()
print "first"
class Second(object):
def __init__(self):
super(Second, self).__init__()
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
很明显如何解决这个方法
Third.__init__() ---> First.__init__() ---> Second.__init__() --->
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"
在 learningpythonthehardway 中,如果没记错的话,我会学到一些叫做 super() 的内置函数。调用 super() 函数可以帮助继承通过父和“兄弟”,并帮助您看得更清楚。我仍然是一个初学者,但我喜欢分享我在 python2.7 中使用这个 super() 的经验。
如果您阅读了本页的评论,您将听到 Method Resolution Order (MRO),该方法是您编写的函数,MRO 将使用 Depth-First-Left-to-Right 方案进行搜索和运行。你可以对此做更多的研究。
通过添加 super() 函数
super(First, self).__init__() #example for class First.
您可以使用 super() 连接多个实例和“家庭”,方法是添加其中的每个人。它会执行这些方法,检查它们并确保你没有错过!但是,在之前或之后添加它们确实会有所不同,如果您完成了 learningpythonthehardway 练习 44,您就会知道。让乐趣开始吧!
以下面的示例为例,您可以复制并粘贴并尝试运行它:
class First(object):
def __init__(self):
print("first")
class Second(First):
def __init__(self):
print("second (before)")
super(Second, self).__init__()
print("second (after)")
class Third(First):
def __init__(self):
print("third (before)")
super(Third, self).__init__()
print("third (after)")
class Fourth(First):
def __init__(self):
print("fourth (before)")
super(Fourth, self).__init__()
print("fourth (after)")
class Fifth(Second, Third, Fourth):
def __init__(self):
print("fifth (before)")
super(Fifth, self).__init__()
print("fifth (after)")
Fifth()
它是如何运行的? Fifth() 的实例将是这样的。每一步都从一个类到添加了超级函数的类。
1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)
父母找到了,它会继续到第三和第四!
5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)
现在所有带有 super() 的类都被访问了!已找到并执行父类,现在它继续在继承中拆箱函数以完成代码。
9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed
上述程序的结果:
fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)
对我来说,通过添加 super() 可以让我更清楚地了解 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
也许还有一些可以添加的东西,一个 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)
对于其他方法也是如此。
在具体类的继承列表中,我将在 ListCreateAPIView
之前添加 LoginToPost
,在 RetrieveUpdateDestroyAPIView
之前添加 LoginToPutOrDelete
。我的具体类的 get
将保持未装饰。
发布此答案以供我将来参考。
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 类是 object
A
应该也调用 __init__
。 A
没有“发明”方法 __init__
,因此它不能假定其他类可能在其 MRO 中更早的时候有 A
。 __init__
方法不(也不应该)调用 super().__init__
的唯一类是 object
。
object
也许我认为,我应该写 class A (object) :
__init__
,则 A
不能是 object
。
不定期副业成功案例分享