Python 调用具有多重继承的父类uu init uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;这条路对吗?
假设我有一个多重继承场景:Python 调用具有多重继承的父类uu init uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;这条路对吗?,python,oop,inheritance,multiple-inheritance,super,Python,Oop,Inheritance,Multiple Inheritance,Super,假设我有一个多重继承场景: class A(object): # code for A here class B(object): # code for B here class C(A, B): def __init__(self): # What's the right code to write here to ensure # A.__init__ and B.__init__ get called? 编写C的\uuuu i
class A(object):
# code for A here
class B(object):
# code for B here
class C(A, B):
def __init__(self):
# What's the right code to write here to ensure
# A.__init__ and B.__init__ get called?
编写C
的\uuuu init\uuu
有两种典型的方法:
ParentClass.\uuuu init\uuuu(self)
super(DerivedClass,self)。\uuu init\uuuu()
A
和B
)(有些可能会丢失,或者被多次调用)
那么正确的方法又是什么呢?说“保持一致,遵循其中一个”很容易,但如果A
或B
来自第三方库,那会怎么样?是否有一种方法可以确保调用所有父类构造函数(并且以正确的顺序,并且只调用一次)
编辑:要了解我的意思,如果我这样做:
class A(object):
def __init__(self):
print("Entering A")
super(A, self).__init__()
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
A.__init__(self)
B.__init__(self)
print("Leaving C")
然后我得到:
Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C
注意,B
的init被调用两次。如果我这样做:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
super(C, self).__init__()
print("Leaving C")
然后我得到:
Entering C
Entering A
Leaving A
Leaving C
请注意,
B
的init永远不会被调用。因此,除非我知道/控制我从中继承的类(A
和B
)的初始值,否则我无法为我正在编写的类(C
)做出安全的选择。这两种方法都可以很好地工作。使用super()
的方法为子类带来了更大的灵活性
在直接调用方法中,C.\uuu init\uu
可以调用A.\uu init\uuu
和B.\uu init\uu
当使用super()
时,类需要设计为协同多重继承,其中C
调用super
,调用A
的代码,后者也将调用super
,后者调用B
的代码。有关如何使用super
的更多详细信息,请参阅
[回答问题,稍后编辑]
因此,除非我知道/控制类的init,否则
继承自(A和B)我不能为我所属的类做出安全的选择
写作(C)
参考文章展示了如何通过在a
和B
周围添加包装类来处理这种情况。在题为“如何合并非合作类”的章节中有一个已制定的示例
有人可能希望多重继承更容易,让您可以毫不费力地编写Car和Planet类来获得FlyingCar,但现实情况是,单独设计的组件通常需要适配器或包装器,然后才能像我们希望的那样无缝地装配在一起:-)
另一个想法是:如果您不喜欢使用多重继承组合功能,您可以使用组合来完全控制在哪些情况下调用哪些方法。如果您要从第三方库中复制子类,则不,调用基类
\uuuu init\uuuu
方法(或任何其他方法)时没有盲目的方法,而不管基类是如何编程的
super
使编写类成为可能,这些类的设计目的是作为复杂多重继承树的一部分协同实现方法,而类作者不需要知道这些树。但无法使用它正确继承可能使用或可能不使用super
的任意类
本质上,一个类是否被设计成使用
super
或直接调用基类的子类是一个属性,它是该类“公共接口”的一部分,并且应该这样记录。如果您以库作者期望的方式使用第三方库,并且该库有合理的文档,它通常会告诉您需要做什么来对特定事物进行子类化。如果没有,那么您必须查看正在子分类的类的源代码,并查看它们的基类调用约定是什么。如果您以库作者没有预料到的方式组合来自一个或多个第三方库的多个类,那么可能根本不可能一致地调用超类方法;如果类A是使用super
的层次结构的一部分,而类B是不使用super的层次结构的一部分,则两个选项都不能保证有效。您必须找出一种适合每种特定情况的策略。本文有助于解释协作多重继承:
它提到了有用的方法mro()
,它显示了方法的解析顺序。在第二个示例中,在A
中调用super
,则super
调用在MRO中继续。顺序中的下一个类是B
,这就是为什么第一次调用B
的init
这里有一篇来自python官方网站的技术性文章:
正如雷蒙德在回答中所说,直接调用
a.\uuuuu init\uuuuu
和B.\uuuu init\uuuuuu
效果很好,您的代码可读
但是,它不使用C
和这些类之间的继承链接。利用这个链接可以使您获得更大的一致性,并使最终的重构更容易,更不容易出错。如何做到这一点的示例:
class C(A, B):
def __init__(self):
print("entering c")
for base_class in C.__bases__: # (A, B)
base_class.__init__(self)
print("leaving c")
您的问题的答案取决于一个非常重要的方面:您的基类是为多重继承设计的吗? 有3种不同的场景:
class Foo:
def __init__(self):
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
重要提示:请注意,Foo
和Bar
都不会调用super()。\uuuu init\uuuu()
!这就是你的代码不能正常工作的原因。由于菱形继承在python中的工作方式,基类为object
的类不应调用super()。正如您所注意到的那样,这样做会使
class Base(object):
def __init__(self):
pass
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
Foo.__init__(self) # explicit calls without super
Bar.__init__(self, bar)
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
super().__init__() # this calls all constructors up to Foo
super(Foo, self).__init__(bar) # this calls all constructors after Foo up
# to Bar
class FooMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # forwards all unused arguments
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
class FooBar(FooMixin, Bar):
def __init__(self, bar='bar'):
super().__init__(bar) # a single call is enough to invoke
# all parent constructors
# NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
# recommended because we don't want to hard-code the parent class.
class CoopFoo:
def __init__(self, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.foo = 'foo'
class CoopBar:
def __init__(self, bar, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.bar = bar
class CoopFooBar(CoopFoo, CoopBar):
def __init__(self, bar='bar'):
super().__init__(bar=bar) # pass all arguments on as keyword
# arguments to avoid problems with
# positional arguments and the order
# of the parent classes
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
# Use super here, instead of explicit calls to __init__
super(C, self).__init__()
print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
# Don't use super here.
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
A.__init__(self)
B.__init__(self)
print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
A.__init__(self)
super(Adapter, self).__init__()
print("<- C")
class C(Adapter, B):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
super(Adapter, self).__init__()
B.__init__(self)
print("<- C")
class C(Adapter, A):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Entering C
Entering A
Leaving A
Entering B
Leaving B
Leaving C
class A:
def __init__(self,a,b,**kwargs):
print("Class A initiallised")
self.a=a
self.b=b
super().__init__(**kwargs)
print("Class A initiallisation done")
def __str__(self):
return f"{self.a} and {self.b}"
def display_a(self):
return f"{self.a} and {self.b}"
class C:
def __init__(self,c,d,**kwargs):
print("Class C initiallised")
self.c=c
self.d=d
super().__init__(**kwargs)
print("class c initiallisation done")
def __str__(self):
return f"{self.c} and {self.d}"
def display_c(self):
return f"{self.c} and {self.d}"
class D(A,C):
def __init__(self,e,**kwargs):
print("Class D initiallised")
super().__init__(**kwargs)
self.e=e
print("Class D initiallisation done")
def __str__(self):
return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"
if __name__=="__main__":
d=D(a=12,b=13,c=14,d=15,e=16)
print(d)
d.display_c()
d.display_a()