首先我在這介紹兩個新的小知識,要在下面用到。一個是函數 id() ,另外一個是運算符 is。id() 函數就是返回對象的內存地址;is 是比較兩個變量的對象引用是否指向同一個對象,在這裏請不要和 == 混了,== 是比較兩個變量的值是否相等。java
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> id(a)
38884552L
>>> a is b
False
>>> a == b
True
複製代碼
copy 這個詞有兩種叫法,一種是根據它的發音音譯過來的,叫拷貝;另外一種就是標準的翻譯,叫複製。python
其實單從表面意思來講,copy 就是將某件東西再複製一份,可是在不少編程語言中,好比 Python,C++中,它就不是那麼的簡單了。c++
>>> a = 1
>>> b = a
>>> b
1
複製代碼
看到上面的例子,從表面上看咱們彷佛是獲得了兩個 1,可是若是你看過我以前寫的文章,你應該對一句話有印象,那就是 「變量無類型」, Python 中變量就是一個標籤,這裏咱們有請 id() 閃亮登場,看看它們在內存中的位置。編程
>>> a = 1
>>> b = a
>>> b
1
>>> id(a)
31096808L
>>> id(b)
31096808L
複製代碼
看出來了嗎,id(a) 和 id(b) 相等,因此並無兩個 1,只是一個 1 而已,只不過是在 1 上貼了兩張標籤,名字是 a 和 b 罷了,這種現象廣泛存在於 Python 之中,這種賦值的方式實現了 「僞裝」 拷貝,真實的狀況仍是兩個變量和同一個對象之間的引用關係。bash
咱們再來看 copy() 方法:編程語言
>>> a = {'name':'rocky','like':'python'}
>>> b = a.copy()
>>> b
{'name': 'rocky', 'like': 'python'}
>>> id(a)
31036280L
>>> id(b)
38786728L
複製代碼
咦,果真此次獲得的 b 和原來的 a 不一樣,它是在內存中又開闢了一個空間。那麼咱們這個時候就來推理了,雖然它們兩個是同樣的,可是它們在兩個不一樣的內存空間裏,那麼確定彼此互不干擾,若是咱們去把 b 改了,那麼 a 確定不變。函數
>>> b['name'] = 'leey'
>>> b
{'name': 'leey', 'like': 'python'}
>>> a
{'name': 'rocky', 'like': 'python'}
複製代碼
結果和咱們上面推理的如出一轍,因此理解了對象有類型,變量無類型,變量是對象的標籤,就能正確推斷出 Python 提供的結果。spa
咱們接下來在看一個例子,請你在往下看的時候保證上面的你已經懂了,否則容易暈車。翻譯
>>> a = {'name':'rocky','like':'python'}
>>> b = a
>>> b
{'name': 'rocky', 'like': 'python'}
>>> b['name'] = 'leey'
>>> b
{'name': 'leey', 'like': 'python'}
>>> a
{'name': 'leey', 'like': 'python'}
複製代碼
上面的例子看出什麼來了嗎?修改了 b 對應的字典類型的對象,a 的對象也變了。也就是說, b = a 獲得的結果是兩個變量引用了同一個對象,可是事情真的這麼簡單嗎?請睜大你的眼睛往下看,重點來了。code
>>> first = {'name':'rocky','lanaguage':['python','c++','java']}
>>> second = first.copy()
>>> second
{'name': 'rocky', 'lanaguage': ['python', 'c++', 'java']}
>>> id(first)
31036280L
>>> id(second)
38786728L
複製代碼
在這裏的話沒有問題,和咱們以前說的同樣,second 是從 first 拷貝過來的,它們分別引用的是兩個對象。
>>> second['lanaguage'].remove('java')
>>> second
{'name': 'rocky', 'lanaguage': ['python', 'c++']}
>>> first
{'name': 'rocky', 'lanaguage': ['python', 'c++']}
複製代碼
發現什麼了嗎?按理說上述例子中 second 的 lanaguage 對應的是一個列表,我刪除這個列表裏的值,也只應該改變的是 second 啊,爲何連 first 的也會改,不是應該互不干擾嗎?是否是很意外?是咱們以前說的不對嗎?那咱們再試試另外一個鍵:
>>> second['name'] = 'leey'
>>> second
{'name': 'leey', 'lanaguage': ['python', 'c++']}
>>> first
{'name': 'rocky', 'lanaguage': ['python', 'c++']}
複製代碼
前面說的原理是有效的,那這究竟是爲何啊,來來來,有請咱們的 id() 再次閃亮登場。
>>> id(first['name'])
38829152L
>>> id(second['name'])
38817544L
>>> id(first['lanaguage'])
38754120L
>>> id(second['lanaguage'])
38754120L
複製代碼
其實這裏深層次的緣由是和 Python 的存儲數據的方式有關,這裏不作過多的說明(實際上是我也不懂。。 在這裏,咱們只須要知道的是,當 copy() 的時候,列表這類由字符串,數字等複合而成的對象仍然是複製了引用,也就是貼標籤,並無創建一個新的對象,咱們把這種拷貝方式叫作淺拷貝(唉呀媽呀,終於把這個概念引出來了。。,言外之意就是並無解決深層次的問題,再言外之意就是還有可以解決深層次問題的方法。
確實,在 Python 中還有一個深拷貝(deep copy),在使用它以前要引入一個 copy 模塊,咱們來試一下。
>>> import copy
>>> first = {'name':'rocky','lanaguage':['python','c++','java']}
>>> second = copy.deepcopy(first)
>>> second
{'name': 'rocky', 'lanaguage': ['python', 'c++', 'java']}
>>> second['lanaguage'].remove('java')
>>> second
{'name': 'rocky', 'lanaguage': ['python', 'c++']}
>>> first
{'name': 'rocky', 'lanaguage': ['python', 'c++', 'java']}
複製代碼
用了深拷貝之後,果真就不是引用了。
更多內容,歡迎關注公衆號「Python空間」,期待和你的交流。