【发布时间】:2013-05-03 15:34:46
【问题描述】:
如果我有这个 Python 代码:
foo = [3, 5, 9]
bar = foo
bar.append(9)
print(foo)
返回
[3, 5, 9, 9]
这肯定意味着当我将9 附加到bar 时,它也影响了foo。如何使变量bar 等于foo,但是当我编辑bar 时,它不会影响foo?
【问题讨论】:
如果我有这个 Python 代码:
foo = [3, 5, 9]
bar = foo
bar.append(9)
print(foo)
返回
[3, 5, 9, 9]
这肯定意味着当我将9 附加到bar 时,它也影响了foo。如何使变量bar 等于foo,但是当我编辑bar 时,它不会影响foo?
【问题讨论】:
使用deepcopy:
>>> from copy import deepcopy
>>> foo = [3, 5, 9]
>>> bar = deepcopy(foo)
>>> bar.append(9)
>>> print(foo)
[3, 5, 9]
>>> print bar
[3, 5, 9, 9]
【讨论】:
在创建原始副本的同时删除对其的所有引用(因此您无法编辑原始副本)时,您使用深层复制。
http://docs.python.org/2/library/copy.html
import copy
bar = copy.deepcopy(foo)
【讨论】:
您正在更改一个可变值,因此您需要进行显式复制:
bar = list(foo)
或
bar = foo[:]
在 Python 中为名称赋值时,您只需将 reference 存储到该值。在不创建列表副本的情况下,foo 和 bar 都引用了同一个列表。
对于非可变值,这不是问题;您替换对字符串的引用以指向不同的值。
但是列表(以及dict 和set 以及大多数类的实例)是可变的。你没有改变foo,你改变了值foo指的是。你做了foo.append(9),Python 将其视为:
foo所指的值。.append。这会返回一个列表实例的方法。9。这会改变列表。Python 名称本身只不过是标签,指向实际值。您可以将值视为气球,将名称视为小纸标签,而分配是将标签绑定到气球的行为。 bar = foo 创建了第二个纸质标签,它与完全相同的气球绑定。
请参阅此older answer of mine,我在其中进一步推动了气球隐喻。
【讨论】:
list(),因为它适用于任何可迭代对象,而且我觉得它更具可读性(这得到了 SO 上询问 [:] 做什么的问题数量的支持)。
copy 模块创建深层副本 (b = copy.deepcopy(a)),或使用列表推导:b = [elem[:] for elem in a]。列表推导式创建每个包含列表的浅表副本,生成一个新的外部列表,其中这些浅表副本作为元素。
你复制一份:
bar = foo[:] #copy of foo
bar.append(9)
这与 python 如何处理赋值有关。分配说“将右侧的对象存储在本地命名空间中左侧的名称下”。因此,在您的情况下,您有 foo 和 bar 引用同一个对象,这就是为什么附加到一个对象也会影响另一个对象。
当使用操作符通常对对象进行操作时,不可变对象会变得更有趣(例如+=)。例如,请参阅my answer here 和更多解释。
【讨论】: