In [1]:
lst = [1, 2, 3]
s = lst
s.pop()
print(lst)
d = {'a': 0}
e = d
e['b'] = 1
print(d)
如果你不是刻意想要这样做(实际也很少会要这样操作),那么就可能导致一些意想不到的错误(尤其是在传递参数给函数的时候)。为了解决这一麻烦,最简单的方法就是不直接变量指向现有的对象,而是生成一份新的 copy 赋值给新的变量,有很多种语法可以实现:
In [2]:
lst = [1,2,3]
llst = [lst,
lst[:],
lst.copy(),
[*lst]] # invalid in 2.7
for i, v in enumerate(llst):
v.append("#{}".format(i))
print(lst)
d = {"a": 0}
dd = [d,
d.copy(),
{**d}] # invalid in 2.7
for i, v in enumerate(dd):
v['dd'] = "#{}".format(i)
print(d)
In [3]:
lst = [0, 1, [2, 3]]
llst = [lst,
lst[:],
lst.copy(),
[*lst]]
for i, v in enumerate(llst):
v[2].append("#{}".format(i))
print(lst)
d = {"a": {"b": [0]}}
dd = [d,
d.copy(),
{**d}]
for i, v in enumerate(dd):
v['a']['b'].append("#{}".format(i))
print(d)
这些 copy 的方法称为浅拷贝(shallow copy),它相比直接赋值更进了一步生成了新的对象,但是对于嵌套的对象仍然采用了赋值的方法来创建;如果要再进一步,则需要深拷贝(deep copy),由标准库 copy
提供:
In [4]:
from copy import deepcopy
lst = [0, 1, [2, 3]]
lst2 = deepcopy(lst)
lst2[2].append(4)
print(lst2)
print(lst)
d = {"a": {"b": [0]}}
d2 = deepcopy(d)
d2["a"]["b"].append(1)
print(d2)
print(d)
清楚了赋值(引用)、copy 还是 deepcopy
之间的区别才能更好地避免意想不到的错误,同样也可以利用它们的特性去实现一些 little tricks,例如我们在 0x04 闭包与作用域 中利用可变对象的特性实现 nonlocal
的功能。关于可变对象的引用、传递等既是 Python 的基本属性,同时又因为隐藏在背后的“暗箱操作”而容易引起误解,想要深入了解可以进一步阅读参考链接的文章,我也会在后面的文章中继续一边学习、一边补充更多这方面的知识。