我在这段代码中遇到了一些意外的行为:
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'
我不明白的是:
- 为什么追加到 B 类中的列表也会追加到 A 类中的列表?
- 为什么在 c 类中附加到该列表会同时附加到 b 和 a?
- 为什么将该变量重新分配给类 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 b
和class 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
绑定到已经存在的对象。这意味着x
和y
现在都绑定到同一个对象,因此,当您执行修改对象的操作(如x.append()
(时,x
和y
都将受到影响。
z = x; z = 'nothing'
不会发生这种情况,因为第二步是创建一个新对象并将z
绑定到它,以便x/y
和z
现在绑定到不同的东西。
它也不会发生在以下任一情况下:
z = x[:]
z = [item for item in x]
因为两者都会创建一个新对象(原始对象(1(的副本(,因此将x
和z
分开。
通过检查每个变量的 ID(可能是您应该使用id
的唯一原因(可以看到效果,为了可读性,删除了公共前缀:
>>> print(id(x)); print(id(y)); print(id(z))
49864
49864
53808
您可以看到x
和y
是同一个对象,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