在Python3中,继承如何与kwargs一起工作?

在Python3中,继承如何与kwargs一起工作?,python,inheritance,python-3.x,keyword-argument,Python,Inheritance,Python 3.x,Keyword Argument,考虑以下示例代码: #!/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 # Acces

考虑以下示例代码:

#!/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()
我想知道如何利用
kwargs
的灵活性,同时仍然使用一些基本的继承。在我正在编写的实际应用程序中,每个类实际上都有许多与之相关联的不同属性

我担心的是,由于
kwargs
非常灵活(而且模糊),我无法维护我的类的父子关系--
data1
data2
data3
,以及
data4
都将应用于
子类

如何确保
data1
data2
父类的属性,而
data3
data4
子类的属性

我试图以“正确的方式”设计我的类结构,同时保持代码的简单和优雅。这有关系吗?也许我走错了路。有更好的方法吗


对不起,我在这里闲逛。只是试着去理解。提前感谢您的帮助和耐心…

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

如果您想确保某事物是某事物的属性,那么它首先必须是一个属性,这通常是通过
self.spam=eggs
赋值来实现的(尽管在某些情况下,您可以通过
setattr
甚至
self.\uu dict\uuuuu.update
来实现)


如何确保data1和data2是父超类的属性,data3和data4是子类的属性

如果确保这是类不变量的一部分,那么您希望在每个类中显式地确保它,并使用
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)
但你可能会注意到,在这里,你并没有从额外的概括性中得到任何好处;你可以也可能应该这样写:

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

尽管如此,我认为你首先是出于误解:

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

不,这些属性都没有应用于任何一个类。它们被应用于
子实例
——但毫无疑问,它们是作为
父实例还是作为
子实例获得这些属性;他们只是通过做一个带有
\uuu dict\uuuu
的对象和一个
self.spam=eggs
赋值来获得它们。无论您将实例视为
父对象
还是
子对象
,这些属性以及在构建后很久添加的随机额外属性、从
对象
继承的标准属性或任何其他属性都存在

如果您真的、真的想强制执行不是子实例的父实例只有数据和数据,那么您可以使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

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)

现在,任何在父实例上分配
data3
属性的尝试都会引发
AttributeError
,除非它实际上是某个子类的实例,该子类添加了
data3
插槽,如
Child
您的第一个问题是,您并没有实际创建属性。如果需要属性,请创建属性。如果需要键值映射,请创建映射。您创建的是一个具有非标准API的特殊用途、有限功能映射。这没有什么好的理由

如果您想确保某事物是某事物的属性,那么它首先必须是一个属性,这通常是通过
self.spam=eggs
赋值来实现的(尽管在某些情况下,您可以通过
setattr
甚至
self.\uu dict\uuuuu.update
来实现)


如何确保data1和data2是父超类的属性,data3和data4是子类的属性

如果确保这是类不变量的一部分,那么您希望在每个类中显式地确保它,并使用
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)
但你可能会注意到,在这里,你并没有从额外的概括性中得到任何好处;你可以也可能应该这样写:

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

尽管如此,我认为你首先是出于误解:

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

不,这些属性都没有应用于任何一个类。它们被应用于
子实例
——但毫无疑问,它们是作为
父实例还是作为
子实例获得这些属性;他们只是通过做一个带有
\uuu dict\uuuu
的对象和一个
self.spam=eggs
赋值来获得它们。无论您将实例视为
父对象
还是
子对象
,这些属性以及在构建后很久添加的随机额外属性、从
对象
继承的标准属性或任何其他属性都存在

如果你真的,真的想强制执行
Parent
insta