你遇到的是 Python 中的引用重复问题。引用自
copy 模块文档:
在 Python 中,赋值语句不会复制对象,它们只是在目标和对象之间创建绑定关系。
如果你从对象和它们的值的角度思考,并使用
is
运算符 和
id()
内置函数,你可以观察到它是如何工作的。
>>> a=1
>>> b=2
>>> c=a
>>> a is c
True
>>> id(a), id(c)
(10932288, 10932288)
>>> id(a), id(c)
除此之外,你还可以通过引用计数来精确地验证相同之处:
>>> import sys
>>> a=1
>>> b=2
>>> sys.getrefcount(a)
803
>>> sys.getrefcount(b)
97
>>> c=a
>>> sys.getrefcount(c)
804
>>> sys.getrefcount(a)
804
>>> c=b
>>> sys.getrefcount(a)
803
>>> sys.getrefcount(b)
98
>>>
顺带一提,这与深复制和浅复制有关。再次引用复制文档:
“浅复制”和“深复制”的区别仅涉及到复合对象(包含其他对象的对象,如列表或类实例):
- “浅复制”构造一个新的复合对象,然后(在可能的范围内)将原始对象中找到的对象的引用插入其中。
- “深复制”构造一个新的复合对象,然后递归地将原始对象中找到的对象的副本插入其中。
您的示例使用简单变量,并且它们始终默认为引用复制 - 无论您尝试进行深度复制,都不会创建新对象。
>>> import copy
>>> id(b),id(c)
(10932320, 10932320)
>>> c = copy.deepcopy(b)
>>> id(b),id(c)
(10932320, 10932320)
然而,如果您尝试分配元组或列表,情况就不同了:
>>> a = [1,2,3]
>>> b = [3,2,1]
>>> c = a
>>> id(a),id(c)
(139967175260872, 139967175260872)
>>> c = copy.deepcopy(a)
>>> id(a),id(c)
(139967175260872, 139967175315656)
在上面的例子中,你得到了一个完全不同的对象。为什么这可能很有用呢?简单赋值只是使两个变量引用同一个对象的事实也意味着,如果你改变其中一个,另一个也会反映出这些变化。
>>> id(c),id(a)
(139967175260872, 139967175260872)
>>> a.append(25)
>>> id(c),id(a)
(139967175260872, 139967175260872)
>>> c
[1, 2, 3, 25]
>>>
当你想要保留原始数据时,这可能是不切实际的。当你想要最初拥有两个相同的对象,但随后让它们以自己的方式改变 - 这就是你想要只为对象本身进行浅复制或为包含在对象中的所有对象进行深复制的地方:
>>> c = copy.deepcopy(a)
>>> a.append(35)
>>> a
[1, 2, 3, 25, 35]
>>> c
[1, 2, 3, 25]
仅供演示目的,浅复制:
>>> c = a
>>> a.append([9,8,7])
>>> a
[1, 2, 3, 25, 35, [9, 8, 7]]
>>> c = a
>>> id(a), id(c), id(a[-1])
(139967175260872, 139967175260872, 139967175315656)
>>> c = copy.copy(a)
>>> id(a), id(c), id(a[-1])
(139967175260872, 139967175315528, 139967175315656)
同一主题下更好的例子请参见grc的精彩回答。
print(id(c)== id(b))
和print(id(c)== id(a))
来验证。现在,c和a之间的任何相似之处都被抹去了。关于名称,请参阅:https://docs.python.org/3/reference/executionmodel.html。我相信在SO上有更详细的答案关于这个话题。 - Anton vBR