Python深浅拷贝教程-面试必问内容
1. 賦值
在Python中,賦值其實就是對象的引用。
a = 9999 b = a print(a) #ouput:9999 print(b) #ouput:9999 print(id(a)) #ouput:1869259054928 print(id(b)) #ouput:1869259054928
這樣賦值后,b和a不僅在值上相等,而且是同一個對象,也就是說在堆內存中只有一個數據對象9999,這兩個變量都指向這一個數據對象。從數據對象的角度上看,這個數據對象有兩個引用,只有這兩個引用都沒了的時候,堆內存中的數據對象9999才會等待垃圾回收器回收。
需要注意的是,它和下面的賦值過程是不等價的:
a = 9999 b = 9999 print(id(a)) #ouput:1869266158672 print(id(b)) #ouput:1869266158768
雖然a和b的值相等,但他們不是同一個對象,這時候在堆內存中有兩個數據對象,只不過這兩個數據對象的值相等。
不可變對象
對于不可變對象,修改變量的值意味著在內存中要新創建一個數據對象
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' >>> a = 9999 >>> b = a >>> id(a) 2625562451792 >>> id(b) 2625562451792 >>> a = 20000 >>> id(a) 2625564836944 >>> id(b) 2625562451792
在a重新賦值之前,b和a都指向堆內存中的同一個數據對象9999,但a重新賦值后,因為數值類型9999是不可變對象,不能在原始內存塊中直接修改數據,所以會新創建一個數據對象保存20000,最后a將指向這個20000對象。這時候b仍然指向9999,而a則指向20000。
可變對象
對于可變對象,比如列表,它是在"原處修改"數據對象的。比如修改列表中的某個元素,列表的地址不會變,還是原來的那個內存對象,所以稱之為"原處修改"。
例如:
>>> L1 = [1,2,3] >>> L2 = L1 >>> L1[0] = 999 >>> L1,L2 ([999, 2, 3], [999, 2, 3]) >>> id(L1) 2625562620872 >>> id(L2) 2625562620872
在L1[0]賦值的前后,數據對象[1,2,3]的地址一直都沒有改變,但是這個列表的第二個元素的值已經改變了。因為L1和L2都指向這個列表,所以L1修改第一個元素后,L2的值也相應地到影響。也就是說,L1和L2仍然是同一個列表對象。
面試中會可能遇到的問題:
1.為啥一直在用9999這個數?
答:
-
9是我的幸運數字😂
-
因為9999不是小整數(移步文章末尾)
2.為什么在Pycharm內的輸出結果和上面內容不一致?
答:首先 Python給[-5,256]以內值分配了空間, 超出的就需要重新分配。 而Pycharm不遵循這個,因為 Pycharm是放到腳本里面編譯的,而不是在解釋器里面,腳本編譯是一次性編譯的會產生編譯文件,所以內存地址會復用,所以輸出的id效果不一致。
2 . 淺拷貝
淺拷貝: 只拷貝第一層的數據。
在python中賦值操作或copy模塊的copy()就是淺拷貝
怎么理解拷貝第一層的數據,先來看一個嵌套的數據結構:
L1 = [1,2,3] L2 = [1,2,[3,33,333]]L1只有一層深度,L2有兩層深度, 淺拷貝時只拷貝第一層的數據作為副本,深拷貝遞歸拷貝所有層次的數據作為副本。
例如:
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' >>> import copy >>> L = [9,99,999] >>> a = [1,2,L] >>> b = copy.copy(a) >>> a,b ([1, 2, [9, 99, 999]], [1, 2, [9, 99, 999]]) >>> id(a),id(b) # 不相等 (2625565288456, 2625565288776) >>> id(a[2]) 2625565288328 >>> id(b[2]) # 相等 2625565288328 >>> L[0] = 0 >>> a,b ([1, 2, [0, 99, 999]], [1, 2, [0, 99, 999]])
a 和 b 是一個獨立的對象,但他們的子對象還是指向統一對象(是引用)。
3 深拷貝
深拷貝:遞歸拷貝所有層次的數據
Python中copy模塊的deepcopy()是深拷貝 ,比如:
>>> L = [9,99,999] >>> a = [1,2,L] >>> b = copy.deepcopy(a) >>> a,b ([1, 2, [9, 99, 999]], [1, 2, [9, 99, 999]]) >>> id(a),id(b) # 不相等 (2625565169224, 2625565169288) >>> id(a[2]) 2625565169480 >>> id(b[2]) # 不相等 2625565169416 >>> L[0] = 0 >>> a,b ([1, 2, [0, 99, 999]], [1, 2, [9, 99, 999]])
深度拷貝, a 和 b 完全拷貝了父對象及其子對象,兩者是完全獨立的。
一般來說,淺拷貝或者引用賦值就是我們所經常用的操作,只有少數情況下( 數據序列化、要傳輸、要持久化 ),才需要深拷貝操作,但是這些操作一般都內置在對應函數中,無需手動去深拷貝。
總結:
- 淺拷貝:內存地址
- 深拷貝:數據內容
4 早已存在的小整數
數值對象是不可變對象,理論上每個數值都會創建新對象。
但實際上并不是這樣,對于 [-5,256]這個區間內的小整數,因為Python內部引用過多,這些整數在python運行的時候就事先創建好并編譯好對象了。所以,a=2, b=2, c=2根本不會在內存中新創建數據對象2,而是引用早已創建好的初始化數值2。
>>> a=2 >>> b=2 >>> a is b True對于超出小整數范圍的數值,每一次使用數值對象都創建一個新數據對象。
例如:
>>> a=9999 >>> b=9999 >>> a is b False但是也有特殊的情況:
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' >>> a=9999;b=9999 >>> a is b True >>> a,b=9999,9999 >>> a is b True為什么會這樣呢?
原因是 Python解析代碼的方式是按行解釋的,讀一行解釋一行,創建了第一個9999時發現本行后面還要使用一個9999,于是b也會使用這個9999,所以它返回True。而前面的換行賦值的方式,在解釋完一行后就會立即忘記之前已經創建過9999的數據對象,于是會為b創建另一個9999,所以它返回False。
如果在Python的文件中執行,則在同一作用域內,a is b是一直會是True的,這和代碼塊作用域有關: 整個py文件是一個模塊作用域 ,可以看我之前的內容
a = 9999 b = 9999 print(a is b) # Truedef func():c = 9999d = 9999print(c is d) # Trueprint(a is c) # Falsefunc()這也是為什么相同的代碼在pycharm和cmd輸出的結果會不一致的原因。
結尾給大家推薦一個非常好的學習教程,希望對你學習Python有幫助!
Python基礎入門教程推薦:更多Python視頻教程-關注B站:Python學習者
【Python教程】全網最容易聽懂的1000集python系統學習教程(答疑在最后四期,滿滿干貨)
總結
以上是生活随笔為你收集整理的Python深浅拷贝教程-面试必问内容的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中定义函数的三种形式
- 下一篇: Python中list复制引发的问题