类中的 Python 列表范围



我在这段代码中遇到了一些意外的行为:

from pprint import pprint
class a (object):
x = ['1']
class b (a):
x = a.x
x.append('2')
class c (a):
x = a.x
x.append('3')
class d (a):
x = a.x
x = 'nothing'
if __name__ == '__main__':
pprint(a.x)
pprint(b.x)
pprint(c.x)
pprint(d.x)

我收到输出:

['1', '2', '3']
['1', '2', '3']
['1', '2', '3']
'nothing'

但我希望收到:

['1']
['1', '2']
['1', '3']
'nothing'

我不明白的是:

  1. 为什么追加到 B 类中的列表也会追加到 A 类中的列表?
  2. 为什么在 c 类中附加到该列表会同时附加到 b 和 a?
  3. 为什么将该变量重新分配给类 d 中的字符串对其他 3 个类没有影响?

你的行

class b (a):
x = a.x

a.x创建另一个"名称",即x(在该范围内(,但它们是同一个对象。如果追加到x,则也会追加到a.x- 它追加到同一对象。

你做一些不同的事情的唯一地方是在

x = 'nothing'

现在将x绑定到另一个对象(字符串(的位置。


如果要将代码更改为

class b (a):
x = a.x.copy()

你会得到不同的行为:也就是说x现在是a.x列表副本的"名称"。

1.2.执行x = a.x时,您只需分配x指向唯一现有的列表,即来自a的列表,因此在操作x时,这会反映在a.x上,因为这是相同的列表,而不是副本。这对class bclass c都是正确的。执行复制

x = list(a.x)

3.执行x='nothing'时,您将一个字符串分配给不再指向列表的字符串x,只是

当你这样定义它时。变量 x 使用父类初始化,继承该类的每个子类都引用该变量(而不是副本(。它成为类名下的全局变量。

要实现预期的输出,请执行以下操作:

from pprint import pprint
class a (object):
def __init__(self):
self.x = ['1']
class b (a):
def __init__(self):
super().__init__()
self.x.append('2')
class c (a):
def __init__(self):
super().__init__()
self.x.append('3')
class d (a):
def __init__(self):
super().__init__()
self.x = 'nothing'
if __name__ == '__main__':
pprint(a().x)
pprint(b().x)
pprint(c().x)
pprint(d().x)

对于新的Python开发人员来说,这可能是最难理解的事情之一,当他们最终理解时,它变成了一个真正的顿悟。你所拥有的没有什么不同:

>>> x = [1]; print(x)
[1]
>>> y = x; print(x); print(y)
[1]
[1]
>>> y.append(2); print(x); print(y)
[1, 2]
[1, 2]
>>> z = x; z = 'nothing'; print(x); print(y); print(z)
[1, 2]
[1, 2]
nothing

原因是,在Python中,一切都是对象,变量只是对象的绑定

因此,x = [1]创建对象[1]并将x变量绑定到该对象

然后,当您执行y = x时,这不会创建新对象,它只是将y绑定到已经存在的对象。这意味着xy现在都绑定到同一个对象,因此,当您执行修改对象的操作(如x.append()(时,xy都将受到影响。

z = x; z = 'nothing'不会发生这种情况,因为第二步是创建一个对象并将z绑定到它,以便x/yz现在绑定到不同的东西。

它也不会发生在以下任一情况下:

z = x[:]
z = [item for item in x]

因为两者都会创建一个新对象(原始对象(1(的副本(,因此将xz分开。

通过检查每个变量的 ID(可能是您应该使用id的唯一原因(可以看到效果,为了可读性,删除了公共前缀:

>>> print(id(x)); print(id(y)); print(id(z))
49864
49864
53808

您可以看到xy同一个对象,z是不同的。

(

1(请记住,如果您的数据结构足够复杂,您可能需要进行深度复制,例如(添加注释(:

>>> x = [[1,2],[3,4]]; y = x; print(id(x)); print(id(y))
773190920  # same object
773190920
>>> y = x[:]; print(id(x)); print(id(y))
773190920  # different objects
832649864
>>> print(id(x[0])); print(id(y[0]))
773088840  # but the sub-objects are still identical.
773088840

最新更新