3

代码如下所示:

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的文档并没有找到解释。

4

3 回答 3

3

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

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

当你这样做时self.y = self.x,你只在实例上创建一个属性,一个实例属性。

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

结果是读取属性的值与分配给它不同。当您读取 的值时self.x,您可能正在检索存储在类中并在所有实例之间共享的值。然而,如果你给 赋值self.x,你总是在赋值给一个实例属性;如果已经有同名的类属性,您的实例属性将隐藏它。

于 2013-03-18T04:18:31.177 回答
0

问题是xandy是不可变的,whilez是可变的,你改变它。

self.z.append()不替换z,它只是添加一个项目到z.

运行C()print C().y, C().z(创建两个不同的C对象),self.z不再计算为,False因为它不再为空。

如果你颠倒你的两条print线,你会发现输出是

1 [1]
2 [1]
于 2013-03-18T04:18:35.593 回答
0

当 Python 评估它的主体时,class A它会实例化一个list对象并将其分配给z. 由于子类不会覆盖它,并且由于list对象在 Python 中通过引用存储,因此所有三个类共享相同的z列表,因此您实例化的第一个类将被填充z,然后其余的只是获取放在那里的任何内容。尽管您更改了列表的内容,但并未更改z指向哪个列表。

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

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

class A(object):
    x = 0
    y = 0
    z = None
    def __init__(self):
        super(A, self).__init__()
        if not self.y:
            self.y = self.x
        if not self.z:
            # create a new list
            z = [self.x]

class B(A):
    x = 1

class C(A):
    x = 2

print C().y, C().z
print B().y, B().z
于 2013-03-18T04:20:57.387 回答