当我实例化一个python子类时,它会覆盖基类的属性

时间:2013-03-18 04:04:46

标签: python class

代码如下:

class A(object):
    x = 0
    y = 0
    z = []
    def __init__(self):
        super(A, self).__init__()
        if not self.y:
            self.y = self.x
        if not self.z:
            self.z.append(self.x)

class B(A):
    x = 1

class C(A):
    x = 2

print C().y, C().z
print B().y, B().z

输出

2 [2]
1 [2]

为什么z会被覆盖而不是y?是因为它不是一个不可变的类型吗?我查看了python的文档,但未找到解释。

3 个答案:

答案 0 :(得分:4)

是的,这是因为一个是不可变的,一个不是。或者更确切地说,就是你正在改变一个而不是另一个。 (重要的不是对象“是否可变”,重要的是你是否实际改变它。)这也是因为你使用的是类变量而不是实例变量(参见this question)。

在类定义中,您将创建三个类变量,在类的所有实例之间共享。创建类的实例后,如果在该实例上执行self.x,它将不会在实例上找到x作为属性,但会在类上查找它。同样地,self.z将查找该类并在类中找到该类。请注意,因为您使z成为一个类变量,所以只有一个列表在所有类的实例之间共享(包括所有子类的所有实例,除非它们覆盖{{1} })。

执行z时,只在实例上创建 new 属性,实例属性。

但是,当您执行self.y = self.x时,不会创建新的实例变量。相反,self.z.append(...)查找存储在类中的列表,然后self.z改变该列表。没有“覆盖”。只有一个列表,当您执行追加时,您正在更改其内容。 (只附加了一个项目,因为你有append,所以在你追加一个之后,这是假的,后续的调用不再附加任何东西。)

结果是读取属性的值与分配属性的值不同。当您读取if not self.z的值时,您可能正在检索存储在类中并在所有实例之间共享的值。但是,如果为self.x分配值,则始终指定实例属性;如果已经有一个具有相同名称的class属性,则您的instance属性将隐藏该属性。

答案 1 :(得分:0)

问题是xy是不可变的,而z是可变的,你可以改变它。

self.z.append()不会替换z,只会将项目添加到z

C()(正在创建两个不同的print C().y, C().z个对象)中运行C后,self.z不再评估为False,因为它不再空。

如果你反转两条print行,你会发现输出是

1 [1]
2 [1]

答案 2 :(得分:0)

当Python评估class A的主体时,它会实例化一个list对象并将其分配给z。由于子类不会覆盖它,并且由于list对象在Python中通过引用存储,因此所有三个类共享相同的z列表,因此您实例化的第一个对象将填充{{1}然后其余的只是得到放在那里的东西。虽然您更改了列表的内容,但您没有更改z引用的列表。

这不会影响z,因为您将整数直接分配到对象的内部字典中,替换以前的值。

要解决此问题,请在构造函数中创建数组,从而分配:

y