項目地址: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!指針