项目地址:https://git.io/pytipspython
Python 中的对象分为两种:可变对象(mutable)和不可变对象(immutable)。不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。在 Python 中,赋值(assignment, =
)的过程仅仅是:git
建立一个(某个值的)对象;github
将变量名指向(引用)这个对象。闭包
这就像 C 语言中指针的概念,只不过更灵活地是 Python 中的变量随时能够指向其它对象(不分类型),其它变量也能够指向这一对象。若是这一对象是可变的,那么对其中一个引用变量的改变会影响其它变量:app
lst = [1, 2, 3] s = lst s.pop() print(lst) d = {'a': 0} e = d e['b'] = 1 print(d)
[1, 2] {'b': 1, 'a': 0}
若是你不是刻意想要这样作(实际也不多会要这样操做),那么就可能致使一些意想不到的错误(尤为是在传递参数给函数的时候)。为了解决这一麻烦,最简单的方法就是不直接变量指向现有的对象,而是生成一份新的 copy 赋值给新的变量,有不少种语法能够实现:函数
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)
[1, 2, 3, '#0'] {'dd': '#0', 'a': 0}
deep
vs shallow
上面给出的这些 copy 的例子比较简单,都没有嵌套的状况出现,若是这里的可变对象中还包含其它可变对象,结果会怎样呢:学习
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)
[0, 1, [2, 3, '#0', '#1', '#2', '#3']] {'a': {'b': [0, '#0', '#1', '#2']}}
这些 copy 的方法称为浅拷贝(shallow copy),它相比直接赋值更进了一步生成了新的对象,可是对于嵌套的对象仍然采用了赋值的方法来建立;若是要再进一步,则须要深拷贝(deep copy),由标准库 copy
提供:spa
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)
[0, 1, [2, 3, 4]] [0, 1, [2, 3]] {'a': {'b': [0, 1]}} {'a': {'b': [0]}}
清楚了赋值(引用)、copy 仍是 deepcopy
之间的区别才能更好地避免意想不到的错误,一样也能够利用它们的特性去实现一些 little tricks,例如咱们在 0x04 闭包与做用域 中利用可变对象的特性实现 nonlocal
的功能。关于可变对象的引用、传递等既是 Python 的基本属性,同时又由于隐藏在背后的“暗箱操做”而容易引发误解,想要深刻了解能够进一步阅读参考连接的文章,我也会在后面的文章中继续一边学习、一边补充更多这方面的知识。.net
欢迎关注公众号 PyHub!指针