Python 之 = [:] copy deepcopy
寫在前面
已經見多很多這樣的Blog,為什么還想自己寫呢?原因很簡單,每當學到一個知識時候,嘗試著對自己或者別人講解這個知識點,當別人能聽懂的時候,就算是真的理解了。
重要部分
首先,要分清楚id()函數、is和= =運算符 的使用以及區別。id()是返回一個對象的地址;is 比較兩個變量的引用是否指向同一個對象;= = 比較兩個變量的值是否相等
其次,Python中一切皆對象,所有的對象擁有三個屬性:id、type、value,Python對象分兩種類型,一是不可變對象,二是可變對象。
不可變對象:該對象所指向的內存中的值不能被改變。一個變量指向的是不可變對象的引用,當對這個變量的值做修改時,由于其所指的值不能被改變,相當于把原來的值復制一份后再改變,這會開辟一個新的地址,這時變量的指向已經發生改變,由原來的指向新開辟的地址。
可變對象:該對象所指向的內存中的值可以被改變。變量改變后,實際上是其所指的值直接發生改變,并沒有發生復制行為,也沒有開辟新的出地址,變量指向的地址并沒有發生改變。
常見的不可變對象:數字、字符串、元組
常見的可變對象:列表、字典、集合
接下來,看一下這個例子
import copy is_same_addr = lambda v1,v2:(id(v1)==id(v2))e = 10 #e指向不可變對象 v_1 = e #直接賦值 v_2 = copy.copy(e) #淺拷貝 print is_same_addr(v_1, e) #>> True print is_same_addr(v_2, e) #>> True顯然,當變量e指向的是不可變對象,采用直接賦值或者拷貝的方式讓另一個變量v等于e,實際上兩個變量指向的是同一個變量的引用,并沒有復制出新的副本
接著,再往下看
e = ['e','jianl', ['Python', 'C++']] v_1 = e #1 賦值 v_2 = e[:] #2 切片 v_3 = copy.copy(e) #3 淺拷貝 v_4 = copy.deepcopy(e) #4 深拷貝print is_same_addr(v_1, e) #>> True print is_same_addr(v_2, e) #>> False print is_same_addr(v_3, e) #>> False print is_same_addr(v_4, e) #>> False可以看出,除了賦值的方式是復制變量e的引用外, 其余方式都是有開辟新的內存,不算是 ‘假復制’,
因此,我想如果v_2 、v_3、 v_4 不小心發生改變,應該不會影響原變量e的值,依據是他們所指向并不是同一個對象, 真的是這樣嗎?敲代碼看一下
似乎是這樣,不過到這里還沒完,接著試一下對列表里的可變對象修改操作,看下結果又會怎樣呢?
v_2[2][0] = 'C#' v_3[2][0] = 'C#'分別對v_2[2] 、v_3[2]所指向的列表作同樣的修改,兩種情況輸出的結果一致,而且變量e的值被修改了
e : ['e', 'jianl', ['C#', 'C++']] v_1: ['e', 'jianl', ['C#', 'C++']] v_2: ['e', 'jianl', ['C#', 'C++']] v_3: ['e', 'jianl', ['C#', 'C++']] v_4: ['e', 'jianl', ['Python', 'C++']]同樣,再對v_4[2]所指向的列表作同樣的修改
v_4[2][0] = 'C#'結果,只有v_4的值被修改了
e : ['e', 'jianl', ['Python', 'C++']] v_1: ['e', 'jianl', ['Python', 'C++']] v_2: ['e', 'jianl', ['Python', 'C++']] v_3: ['e', 'jianl', ['Python', 'C++']] v_4: ['e', 'jianl', ['C#', 'C++']]為什么會這樣呢?來做一個驗證, 對比變量指向的對象成員中為可變變量的地址
print is_same_addr(v_1, e),is_same_addr(v_1[2], e[2]) #True True print is_same_addr(v_2, e),is_same_addr(v_2[2], e[2]) #False True print is_same_addr(v_3, e),is_same_addr(v_3[2], e[2]) #False True print is_same_addr(v_3, e),is_same_addr(v_4[2], e[2]) #False False水落石出,利用切片[:]屬性或者copy()函數實現對一個變量賦值,屬于淺拷貝,只是拷貝對象的一部分不可變類型的數據,對于對象里的可變類型對象屬性,僅僅是拷貝一個引用;而使用deepcopy()函數實現的是深拷貝,將變量所指向的對象整個復制,開辟新的空間,再由另一個變量指向這塊新的空間。
最后
幾句話總結:
1 對象有類型,變量無類型,變量是對象的標簽;
2 對指向不可變對象變量的值作修改,實際上是改變變量的指向,對象本身是不可能被修改的;指向可變對象變量的值發生改變,實際上是對象本身被修改了;
3 v = e,實際上是將e的引用賦值給v, v e指向同一個變量;
4 v= e[:] 及 v=copy(e),淺拷貝,只復制部分數據,而e所指向的對象中可變類型屬性,只是拷貝引用;
5 v = deepcopy(e), 深拷貝,e指向的整個對象被復制,再由v指向;
參考文章:
https://www.jianshu.com/p/9ed9b5ce7bb0
總結
以上是生活随笔為你收集整理的Python 之 = [:] copy deepcopy的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mysql联合索引失效,需注意索引的最左
- 下一篇: 计算机操作系统实验指导 (第3版) 第四