Maintaining readability when using super() for direct multiple inheritance
对于最基本的多重继承:
1 2 3 4 5 6 7 8 9 10 11 12
| class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, b):
self.b = b
class C(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b) |
我不明白为什么要使用super()。我想您可以用Kwargs实现它,但是这肯定比上面的方法更不可读。我还没有找到任何支持这种方法的关于堆栈溢出的答案,但对于这种情况,这肯定是最令人满意的吗?
在这个问题上,有很多问题被标记为重复的,但是对于这个确切的案例,没有令人满意的答案。这个问题涉及多重继承和使用super()进行钻石继承。在这种情况下,没有菱形继承,父类之间也没有任何知识,因此它们不需要像这样调用super()。
这个答案处理在这个场景中使用super,但不向__init__传递参数,就像这里所做的那样,这个答案处理传递参数,但又是一个菱形继承。
- 使用super的一个好处是代码可维护性。一旦A和B的名称更改,代码将中断
- 公平点-你能演示一下如何在这个确切的例子中通过参数来实现super吗?
- 由于父类必须在继承类中显式声明,因此如果更改类名,代码肯定仍然会中断。优点是我可以在init中少更改两行?
- 你不必使用super;事实上,不这样做是一个合理的选择。并非所有的类都需要支持合作的多重继承。super的目的并不是为了避免必须键入基类的名称;而是实现一个特殊的多重继承模型。
这里正确使用EDOCX1[0]的方法是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class A:
def __init__(self, a, **kwargs):
super().__init__(**kwargs)
self.a = a
class B:
def __init__(self, b, **kwargs):
super().__init__(**kwargs)
self.b = b
class C1(A, B):
pass
class C2(A, B):
def __init__(self, a, b, **kwargs):
super().__init__(a=a, b=b, **kwargs)
c1 = C1(a="foo", b="bar")
c2 = C2(a="foo", b="bar") |
C的方法分辨率顺序为[C, A, B, object]。每次调用super()时,它都会根据当时调用super()的位置返回MRO中下一个类的代理。
定义C时有两个选项,这取决于您是否要定义带有签名的C.__init__,签名中提到初始化所需的A和B两个参数。使用C1,没有定义C1.__init__,因此将调用A.__init__。使用C2时,需要显式调用链中的下一个__init__方法。
C知道它是A和B的一个子类,必须至少为已知的上游__init__方法提供预期的论据。
A.__init__将把除A以外的所有东西传递给下一个类的__init__方法。
除B外,B.__init__将把它收到的所有东西都传出去。
最终将调用object.__init__,并且假设所有以前的类都正确地删除了它们引入的关键字参数,那么将不会收到其他参数。
更改各种__init__的调用顺序意味着更改mro,这意味着更改基类的顺序。如果你想要更多的控制权,那么合作的多重继承就不适合你了。
- C2看起来很有希望,但在实例化时,TypeError: __init__() takes 2 positional arguments but 3 were given得到了
- 固定的。虽然可以向重写函数添加位置参数,但最好使用关键字参数来调用函数。(主要是因为这是唯一一种接受意料之外的论点的方法。)请注意,对于C2并不知道super()指的是A,正如本例中的A不知道它对super()的调用引用了B,而不是object,例如C2。
- 当调用上游方法时,super有效地防止您关心self的运行时类型。
- 好极了!我想知道解决办法是否在于提供额外的禁运,但没有想到在每一个阶段都能像这样移除它们。这正是我想要的,并且是一个很好的模型,可以在为父类提供参数的同时使用super。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class A(object):
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
self.a = kwargs['a']
class B(object):
def __init__(self, *args, **kwargs):
super(B, self).__init__()
self.b = kwargs['b']
class C(A, B):
def __init__(self, *args, **kwargs):
super(C, self).__init__(*args, **kwargs)
z = C(a=1,b=2)
z.b
2 |
- 这真的是首选的实现方法吗?现在必须用父类所需参数的明确知识初始化C。
- 离开一段时间后回到这篇文章,我想再次实现这一点,但我仍然不能相信这是首选的方法。想要实例化C的用户绝对无法知道应该传递什么参数。
- @这是重新使用A和B的成本。你希望C()做什么?
- 在实例化C时,我希望能够看到所需的参数,而不仅仅是*args, **kwargs。
- A和B都需要从对super().__init__的调用中删除各自的参数,以避免object.__init__被意外的参数调用。
- 我理解这一点,但我想指出的是,因为我们使用了super而不是直接实例化父类,所以我们现在有了一个更不可用的继承类。
- 你能再解释一下你的代码吗?