代码如下:
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的文档,但未找到解释。
答案 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)
问题是x
和y
是不可变的,而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