Python 3 中的 kwargs 继承如何工作?

1 python inheritance keyword-argument python-3.x

考虑以下示例代码:

#!/usr/bin/python3

class Parent:
    def __init__(self, **kwargs):
        # Expect data1, data2
        self.values = kwargs

    # Accessor method to set object values
    def setvalue(self, k, v):
        self.values[k] = v

    # Accessor method to get/return object values
    def getvalue(self, k):
        return self.vlaues.get(k, None)

class Child(Parent):
    def __init__(self, **kwargs):
        # Expect data3, data4
        self.values = kwargs

    # Accessor method to set object values
    def setvalue(self, k, v):
        self.values[k] = v

    # Accessor method to get/return object values
    def getvalue(self, k):
        return self.values.get(k, None)

def main():
    new_object = Child(data1 = 'abc', data2 = 'def', 
        data3 = 'ghi', data4 = 'jkl')

    for v in new_object.values:
        print('{}: {}'.format(v, new_object.getvalue(v)))

    new_object.setvalue('data1', 'tuv')
    new_object.setvalue('data4', 'xyz')

    for v in new_object.values:
        print('{}: {}'.format(v, new_object.getvalue(v)))

if __name__ == "__main__": main()
Run Code Online (Sandbox Code Playgroud)

我想知道如何kwargs在仍然使用一些基本继承的同时利用灵活性。在我正在编写的实际应用程序中,每个类实际上都有许多与之相关的不同属性。

我担心的是,因为kwargs是非常灵活(并且模糊不清),我不能保持我的课的亲子关系- ,data1data2data3并且data4将全部得到应用到Child类。

我如何确保data1data2Parent超类的属性,以及data3data4Child子类的属性?

我正在尝试以“正确的方式”设计我的类结构,同时保持代码的简单和优雅。它甚至重要吗?也许我在这方面走错了路。有一个更好的方法吗?

对不起,如果我在这里闲逛。只是试图理解。预先感谢您的帮助和耐心...

aba*_*ert 5

您的第一个问题是您实际上并没有首先创建属性。如果需要属性,请创建属性。如果需要键值映射,请创建映射。您创建的是具有非标准 API 的特殊用途、功能有限的映射。没有充分的理由这样做。

如果你想确保某物是某物的一个属性,它首先必须是一个属性,你通常用self.spam = eggs赋值来做(尽管在某些情况下你用setattr或 甚至来做self.__dict__.update)。


如何确保 data1 和 data2 是 Parent 超类的属性,而 data3 和 data4 是 Child 子类的属性?

如果确保它是类的不变量的一部分,您希望在每个类中明确地确保它,并将任何剩余的参数与super. 例如:

class Parent:
    def __init__(self, **kwargs):
        self.data1 = kwargs.pop('data1', None)
        self.data2 = kwargs.pop('data2', None)
        super().__init__(**kwargs)

class Child(Parent):
    def __init__(self, **kwargs):
        self.data3 = kwargs.pop('data3', None)
        self.data4 = kwargs.pop('data4', None)
        super().__init__(**kwargs)
Run Code Online (Sandbox Code Playgroud)

但是您可能会注意到,您并没有真正从这里的额外通用性中获得任何好处;你可以——而且可能应该——像这样写同样的东西:

class Parent:
    def __init__(self, *, data1=None, data2=None, **kwargs):
        super().__init__(**kwargs)
        self.data1 = data1
        self.data2 = data2

class Child(Parent):
    def __init__(self, *, data3=None, data4=None, **kwargs):
        super().__init__(**kwargs)
        self.data3 = data3
        self.data4 = data4
Run Code Online (Sandbox Code Playgroud)

话虽如此,我认为您首先是出于误解进行操作:

我担心的是,由于 kwargs 非常灵活(而且模糊不清),我无法维护我的类的父子关系——data1、data2、data3 和 data4 都将应用于 Child 类。

不,这些属性都没有应用于任一类。它们被应用于每个实例Child——但毫无疑问,它们是作为Parent实例还是作为Child实例获得这些属性;他们只是通过成为带有 a 的对象__dict__并进行self.spam = eggs分配来获得它们。无论您将实例视为 aParent还是 a Child,这些属性都在那里——以及您在构造后很长时间添加的随机额外属性,或从 继承的标准属性object,或其他任何东西。

如果你真的,真的想强制那些Parent不是Child只有dataand 的实例data2,你可以用__slots__, @property,显式描述符来做到这一点......让我们做第一个,因为它在这里最简单和最清晰:

class Parent:
    __slots__ = ('data1', 'data2')
    def __init__(self, **kwargs):
        self.data1 = kwargs.pop('data1', None)
        self.data2 = kwargs.pop('data2', None)
        super().__init__(**kwargs)

class Child(Parent):
    __slots__ = ('data3', 'data4')
    def __init__(self, **kwargs):
        self.data3 = kwargs.pop('data3', None)
        self.data4 = kwargs.pop('data4', None)
        super().__init__(**kwargs)
Run Code Online (Sandbox Code Playgroud)

现在,任何data3Parent实例上分配属性的尝试都会引发AttributeError,除非它实际上是某个添加data3插槽的子类的实例,例如Child.