kwargs 的多重继承

Multiple Inheritance with kwargs

问题

我在 面向对象编程 中遇到了这段代码 Dusty Phillips(为简洁起见进行了简化),我不确定其中的特定部分这个定义。

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 C(A, B):
    def __init__(self, c, **kwargs):
        super().__init__(**kwargs)
        self.c = c

问题

  1. 既然方法解析顺序是(__main__.C, __main__.A, __main__.B, object),那么class B是否可以定义成下面的方式呢?
class B:
    def __init__(self, b):
        self.b = b
  1. class B 中的 super().__init__(**kwargs) 是不是多余的,因为任何多余的 kwargs 传递给 C 都会传递给 object,提高?

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

  1. 如果 C 被定义为 class C(B, A) 而不是 class C(A, B),这是一种安全措施吗?

在这个例子中,如果 B 按照您所说的定义(第 1 点)并且 C 是原样(并且没有其他用途),那么它的工作原理是一样的。

至于第 2 点:调用 super() 的构造函数确实会失败,如果仍然有关键字参数,例如:

c = C(a=1, b=2, c=3, d=4)
# -> TypeError: object.__init__() takes exactly one argument (the instance to initialize)

因为class B是(本来)写的,如果按照你说的倒序使用,或者有一个(或)多个超级class(es),例如:

class D:
    def __init__(self, d, **kwargs):
        super().__init__(**kwargs)
        self.d = d

class C(A,B,D):
    ...

考虑如何实例化 C:

c = C(a=3, b=5, c=9)

C.__init__ 获取 所有 关键字参数,但仅将一个用于其自己的参数 c。其余的传递给链中的下一个 __init__ 方法。在本例中,即 A.__init__,其中 "pulls out" 是 a 的参数,并将 b 传递给 B.__init__B 使用它并将(现在为空的)关键字参数集传递给 next 方法 object.__init__。因为所有关键字参数都已 "claimed" 并由其他 类 处理,所以 object.__init__ 成功。

由于 MRO 的构建方式,正确使用 super() 的 类 保证 共同 **kwargs 届时将为空object.__init__ 被调用。

Since the method resolution order is (__main__.C, __main__.A, __main__.B, object), could class B be defined in the following way instead?

不,因为这样会失败:

class D:
    def __init__(self, d, **kwargs):
        self.d = d
        super().__init__(**kwargs)

class E(C, D):
    def __init__(self, e, **kwargs):
        self.e = e
        super().__init__(**kwargs)

E的MRO是(E, C, A, B, D, object),所以B必须调用super().__init__否则D.__init__不会被调用

Isn't super().__init__(**kwargs) in class B redundant, since any surplus kwargs passed to C will be passed to object, raising?

不会,因为在上面的例子中,多余的kwargs会转到D.__init__。但即使没有它,当你调用带有太多参数的构造函数时引发错误也不是多余的;最好有一条错误消息通知您您的错误代码,而不是让错误未被发现。

Is this a safeguard for if C was defined as class C(B, A) instead of class C(A, B)?

从某种意义上说,当然;但实际上它是对 any class 层次结构中发生的 B 的保护措施,只要层次结构中的其他 classes 遵循相同的规则呼叫 super().__init__(**kwargs).