摘要:
>>> a.append(8)
>>> a,d
([1, 2, 4, 5, 6, ['a', 'b', 'c'], 7, 8], [1, 2, 4, 5, 6, ['a', 'b', 'c'], 7])
>>> a[5].append('d')
>>> a,d
([1, 2, 4, 5, 6, ['a', 'b', 'c', 'd'], 7, 8], [1, 2, 4, 5, 6, ['a', 'b', 'c', 'd'], 7])
>>>
>>> a.append(3)
>>> a,d
([1, 2, [1, 2], 3], [1, 2, [1, 2]])
>>> a,d
([1, 2, [1, 2, 3], 3], [1, 2, [1, 2]])
python中的一切事物皆為對象,並且規定參數的傳遞都是對象的引用。可能這樣說聽起來比較難懂,對比一下PHP中的賦值和引用就有大致的概念了。參考下面一段引用:
1.Python不允許程序員選擇采用傳值還是傳 引用。Python參數傳遞采用的肯定是“傳對象引用”的方式。實際上,這種方式相當於傳值和傳引用的一種綜合。如果函數收到的是一個可變對象(比如字典 或者列表)的引用,就能修改對象的原始值——相當於通過“傳引用”來傳遞對象。如果函數收到的是一個不可變對象(比如數字、字符或者元組)的引用,就不能 直接修改原始對象——相當於通過“傳值”來傳遞對象。
2.當人們復制列表或字典時,就復制了對象列表的引用同,如果改變引用的值,則修改了原始的參數。
3.為了簡化內存管理,Python通過引用計數 機制實現自動垃圾回收功能,Python中的每個對象都有一個引用計數,用來計數該對象在不同場所分別被引用了多少次。每當引用一次Python對象,相 應的引用計數就增1,每當消毀一次Python對象,則相應的引用就減1,只有當引用計數為零時,才真正從內存中刪除Python對象。
所謂“傳值”也就是賦值的意思了。那么python參數傳遞有什么特殊呢?看例子:
>>> seq = [1, 2, 3] >>> seq_2 = seq >>> seq_2.append(4) >>> print seq, seq_2 [1, 2, 3, 4] [1, 2, 3, 4] >>> seq.append(5) >>> print seq, seq_2 [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
如果按照PHP的語法,seq和seq_2這兩個變量對應兩個不同的存儲地址,自然對應不同的值,是毫無關聯的,但是在python中確令我們大跌眼鏡。再看下面的例子:
>>> a = 1 >>> b = a >>> b = 2 >>> print a, b 1 2 >>> c = (1, 2) >>> d = c >>> d = (1, 2, 3) >>> print c, d (1, 2) (1, 2, 3)
顯然和上面的例子有沖突嗎?看開頭引用的話就明白了,當引用的原始對象改變的時候,他倆就沒有關系了,也就是說他倆是兩個不同對象的引用,對應各自引用計 數加減1;而第一個例子中seq和seq_2都是對原始對象[1, 2, 3]這個lis對象的引用,所以不管append()還是pop()都不會改變原始對象,只是改變了它的元素,這樣也就不難理解第二個例子了,因為b = 2就是創建了一個新的 int 對象。
接下來再通過例子看copy與deepcopy的區別:
>>> seq = [1, 2, 3] >>> seq_1 = seq >>> seq_2 = copy.copy(seq) >>> seq_3 = copy.deepcopy(seq) >>> seq.append(4) >>> print seq, seq_1, seq_2, seq_3 [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3] [1, 2, 3] >>> seq_2.append(5) >>> print seq, seq_1, seq_2, seq_3 [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 5] [1, 2, 3] >>> seq_3.append(6) >>> print seq, seq_1, seq_2, seq_3 [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 5] [1, 2, 3, 6]
這個例子看不出copy之后和之前的聯系,也看不出copy與deepcopy的區別。那么再看:
>>> m = [1, ['a'], 2] >>> m_1 = m >>> m_2 = copy.copy(m) >>> m_3 = copy.deepcopy(m) >>> m[1].append('b') >>> print m, m_1, m_2, m_3 [1, ['a', 'b'], 2] [1, ['a', 'b'], 2] [1, ['a', 'b'], 2] [1, ['a'], 2] >>> m_2[1].append('c') >>> print m, m_1, m_2, m_3 [1, ['a', 'b', 'c'], 2] [1, ['a', 'b', 'c'], 2] [1, ['a', 'b', 'c'], 2] [1, ['a'], 2] >>> m_3[1].append('d') >>> print m, m_1, m_2, m_3 [1, ['a', 'b', 'c'], 2] [1, ['a', 'b', 'c'], 2] [1, ['a', 'b', 'c'], 2] [1, ['a', 'd'], 2]
從這就看出來區別了,copy拷貝一個對象,但是對象的屬性還是引用原來的,deepcopy拷貝一個對象,把對象里面的屬性也做了拷貝,deepcopy之后完全是另一個對象了。再看一個例子:
>>> m = [1, [2, 2], [3, 3]] >>> n = copy.copy(m) >>> n[1].append(2) >>> print m, n [1, [2, 2, 2], [3, 3]] [1, [2, 2, 2], [3, 3]] >>> n[1] = 0 >>> print m, n [1, [2, 2, 2], [3, 3]] [1, 0, [3, 3]] >>> n[2].append(3) >>> print m, n [1, [2, 2, 2], [3, 3, 3]] [1, 0, [3, 3, 3]] >>> m[1].pop() 2 >>> print m, n [1, [2, 2], [3, 3, 3]] [1, 0, [3, 3, 3]] >>> m[2].pop() 3 >>> print m, n [1, [2, 2], [3, 3]] [1, 0, [3, 3]]
最后測試你到底掌握沒有:
l = [] d = {'num': 0, 'sqrt': 0} for x in [1, 2, 3]: d['num'] = x d['sqrt'] = x*x l.append(d) print l
由於我主要從事WEB開發,平時主要用framework里面一下package,很少研究python自身的一些東西,不過話說回來,我是用python來工作的,也不是專門研究這門語言的教授之類。時間越久發現python越有趣。
轉自:http://luchanghong.com/python/2012/09/21/the-differences-between-copy-and-deepcopy-in-python.html