如何在Python中继承和扩展类属性?

14

我在网络上做了很多调研,但没有找到在子类中扩展“class”属性字典的正确方法。大部分文档都是在方法内扩展属性。

我尝试了dictionary.update(),但它不起作用。

这是我的示例:

class Super(object):
    dictionary = {'one':1, 'two':2}

    def __init__(self, var):
        self.var = var

    def supermethod(self):
        pass

而我将其扩展到:

class Subclass(Super):
    dictionary.update({"zero":0})

    def __init__(self, var):
        super(Subclass, self).__init__(var)
        self.var = var

    def submethod(self):
        pass

如果我覆盖字典 - 它可以正常运行。但是如果我尝试扩展它,会出现以下错误:
AttributeError: 'Subclass'对象没有'dictionary'属性
3个回答

14

在 Python 中,class 是一个可执行语句。当解释器找到 class 语句时,首先会执行代码块中的所有代码(在一个特殊的命名空间中),然后使用该块中定义的所有名称来构建 class 对象(Python 类是对象),最后将类名绑定到当前范围内的类对象。

也就是说,在类语句块内,class 对象尚不存在,因此无法引用它,无论是显式引用(通过类名)还是隐式引用(Python 过份强调显式而不是隐式)。

但是,父类对象在这一点上显然是存在的(否则你就无法从它继承),因此你可以显式引用它:

class Parent(object):
   attr = something()


class Child(Parent):
   attr = Parent.attr
   # do something with Parent.attr
注意在类语句块中定义的属性(这里是attr)是“类属性”,也就是class对象的属性,因此在实例之间共享。当这个属性是可变对象时,从一个实例中改变它将影响所有实例。
还要记住,Python从不隐式复制任何东西,所以以下代码:
class Parent(object):
   attr = {"foo": "bar"}


class Child(Parent):
   attr = Parent.attr
   attr["baaz"] = "quux"
   # or attr.update(baaz="quux") etc

将会更新Parent.attr


2
谢谢。你解释的方式让我对这个问题有了更清晰的认识:“请记住,Python从不隐式复制任何内容,因此下面的代码将更新Parent.attr。” - Claudiu

5
子类从来不会将它们的超类属性作为它们自己的属性,无论是方法还是其他。
class Subclass(Super):
  dictionary = Super.dictionary
  dictionary.update({zero:0})

8
子类确实继承了它们的超类属性(更确切地说,可以通过子类访问超类属性),但不包括在class语句块中。 - bruno desthuilliers
>>> class B: ... foo = 42 ... >>> class D(B): ... pass ... >>> B.__dict__ mappingproxy({'__module__': '__main__', 'foo': 42, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}) >>> D.__dict__ mappingproxy({'__module__': '__main__', '__doc__': None}) - Ignacio Vazquez-Abrams
@brunodesthuilliers:是的,这是因为Object.__getattribute__()会遍历MRO。foo仍然不是D的一部分。 - Ignacio Vazquez-Abrams
是的,那就是我说的,我知道它是如何工作的(自1.5.2版本以来就一直在使用Python,并且对Python的对象模型有很多乐趣)。 - bruno desthuilliers

5
子类拥有其父类的属性作为它们自己的属性。 您不能使用 dictionary.update({"zero":0}),因为此时 class Subclass 还不存在。 如果您不执行第一行和第二行代码,您仍然可以看到第四行打印出 {'one':1, 'two':2},这证明了这一点。
但是,如果您执行第一行和第二行代码,您必须添加 copy(),否则您将看到第三行和第四行都变成了 {'zero': 0, 'two': 2, 'one': 1},这意味着您想要扩展子类的参数,但同时也修改了父类中的参数,这是不合理的。
所以下面的代码只会输出:

{'two': 2, 'one': 1}

{'zero': 0, 'two': 2, 'one': 1}

我认为这符合您的要求。
class Super(object):
    dictionary = {'one':1, 'two':2}

    def __init__(self, var):
        self.var = var

    def supermethod(self):
        pass

class Subclass(Super):
    dictionary = Super.dictionary.copy() # line1
    dictionary.update({"zero":0}) # line2

    def __init__(self, var):
        super(Subclass, self).__init__(var)
        self.var = var

    def submethod(self):
        pass

print(Super.dictionary) # line3
print(Subclass.dictionary) # line4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接