在类继承链中传递kwarg

时间:2019-03-11 21:08:08

标签: python inheritance kwargs method-resolution-order

我有以下设置:

class A:
  def __init__(self, **kwargs):
    # Some variables initialized
    for k, v in kwargs.items():
        setattr(self, k, v)

class B(A):
  def __init__(self, **kwargs):
    A.__init__(self, **kwargs)
    self._b = {}
    for k, v in kwargs.items():
        setattr(self, k, v)

    @property
    def b(self):
        return self._b

    @b.setter
    def b(self, value):
        self._b.update(value)

class C(B):
    def __init__(self, **kwargs):
    B.__init__(self, **kwargs)
    # Some variables initialized
    for k, v in kwargs.items():
        setattr(self, k, v)

当我现在创建C的新实例时,出现以下错误:

AttributeError: 'C' object has no attribute '_b'

现在这很有意义,因为在调用B._b时尚未初始化A.__init__(self, **kwargs)。我可以像这样简单地通过重新排序B的初始化来解决此问题,

class B(A):
  def __init__(self, **kwargs):
    self._b = {}
    A.__init__(self, **kwargs)
    for k, v in kwargs.items():
        setattr(self, k, v)

我想了解在初始化过程中需要将kwargs从子类传递到父类时是否有推荐/最佳实践方法?在我看来,以下几件事会起作用,

  1. 像上面一样重新排序初始化
  2. 在每个子类中分配kwargs,然后弹出它们,并将其余的kwargs传递给父级初始化
  3. 好些

希望为3获得一些方法。

1 个答案:

答案 0 :(得分:0)

您遇到的问题与这些循环有关:<​​/ p>

for k, v in kwargs.items():
    setattr(self, k, v)

每个类中都有一个,这意味着每个所有之一都将 all 关键字参数设置为self上的属性。

当该循环在A中运行时,它失败了,因为B的属性需要初始化才能起作用。

正如您在问题中所指出的,快速解决方案是确保B在运行A.__init__之前设置其字典:

class B(A):
    def __init__(self, **kwargs):
        _b = {}                        # set this up first
        A.__init__(self, **kwargs)     # before calling the superclass
        for k, v in kwargs.items():
            setattr(self, k, v)

但是可能有更好的方法可以让您避免冗余循环。我建议明确命名每个类中期望的关键字参数。这样,b只会被B类看到,而不会被AC看到(除了作为kwargs的一部分)。

class A:
    def __init__(self, *, a): # a is keyword-only arg, no kwargs accepted here
        self.a = a

class B:
    def __init__(self, *, b, **kwargs):
        super().__init__(**kwargs) # doesn't mess with b!
        self._b = {}
        self.b = b

    @property
    def b(self):
       ...

class C:
    def __init__(self, *, c, **kwargs):
        super().__init__(**kwargs)
        self.c = c

现在您可以调用C(a="foo", b={1: 2}, c="bar"),每个类将仅关注其关注的属性。