1.幾種拷貝方式的比較
from copy import deepcopy
a = [1,2,[3,4,5]]
a1 = a #直接賦值,傳引用
a2 = a.copy() #shallow copy
a3 = deepcopy(a) #deep copy
a4 = a[:] #slice: shallow copy
a[0] = 6 #改變原物件中的可變型別和不可變型別
a[2][0] = 7
# print(a)
# print(a1)
# print(a2)
# print(a3)
# print(a4)
#輸出結果:
# [6, 2, [7, 4, 5]]
# [6, 2, [7, 4, 5]] #直接傳引用,導致原物件和引用物件指向同一片記憶體區域,修改同步
# [1, 2, [7, 4, 5]] #淺拷貝,對於不可變物件,修改時會改變對應元素,但可變物件不會
# [1, 2, [3, 4, 5]] #深拷貝,遞迴地拷貝內部所有元素,因而是獨立部分
# [1, 2, [7, 4, 5]] #這裡切片實現的是 淺拷貝
可以注意到,直接的賦值語句是傳遞引用,進行變數關聯,而list 本身的copy() 方法是實現淺拷貝的工作。
2. 切片賦值
對於切片賦值,請看原理:
list 本身會有__setitem__的方法,檢視是不是傳入slice物件(isinstance),如果是slice物件,通過slice物件的start,stop,step索引值進行賦值
本質上,容器型別,都會有對應的__getitem__ / __setitem__的方法,get/set對應的需求的值。
這裡就可以發現,實際上是對於源陣列本身的物件位址開始賦值(個人猜測,memcpy是體現深拷貝的地方)。
驗證這種方法對於右端待複製值是深拷貝:
a = [1,2,[3,4,5]]
b = [6,7,8]
a[0:2] = b
print(a)
print(b)
b[0] = 9
print(a)
print(b)
# 輸出結果:
# [6, 7, 8, [3, 4, 5]]
# [6, 7, 8]
# [6, 7, 8, [3, 4, 5]]
# [9, 7, 8]
總結:使用a[:]=b的格式時,會對右側物件進行深拷貝,通過內部方法實現對於slice對應的部分進行設定。
3. 其他驚喜發現
為什麼python的slice本身是淺拷貝?
這裡的for迴圈對於src和dect的賦值,看起來是淺拷貝的形式,沒有發現對應的深拷貝。
4. 函式內部的可變物件傳參
# -*- coding: utf-8 -*-
"""created on wed feb 16 19:09:32 2022
@author: [email protected]
"""def change_assign(a):
res =
a = res
def change_slice_assign(a):
res =
a[:] = res
a = [1,2]
change_assign(a)
print(a) #輸出的是原來的a,沒有經過改變
change_slice_assign(a)
print(a) #輸出的是改變的a,因為可變型別傳參,更像是c++裡面的引用傳參機制
在編輯這段**的時候,ide同時也給出了提示資訊(spider nb)
這裡的 a 已經不是作為原來傳進來的 a 處理了,而是相當於新建了區域性變數 a 作為 res 的引用
python 處理變數搜尋具有legb原則,參考:
所以沒有對 a 進行修改,導致看起來是可變引數沒有修改,而實際上是生成了物件引用的本地變數,函式結束時就不存在了。
而下面的change_slice_assign則使用之前提到的切片淺拷貝賦值,實現了對於res的拷貝。
另外,補充一點 在函式中的 += 和 = 也有類似的區別問題
def change_add(a):
a = a + a
def change_iadd(a):
a += a
a = [1,2]
change_add(a)
print(a) #輸出的是原來的a,沒有經過改變
change_iadd(a)
print(a) #輸出的是改變的a,因為+= __iadd__: in-place add
如果進行修改:
a[:] = a + a
則可以解決這一問題。
python中的可變物件和不可變物件
知識點 python中,萬物皆物件。知識點 python中,萬物皆物件。python中不存在所謂的傳值呼叫,一切傳遞的都是物件的引用,也可以認為是傳址。python在heap中分配的物件分成兩類 可變物件和不可變物件。所謂可變物件是指,物件的內容可變,而不可變物件是指物件內容不可變。不可變 immu...
python中的可變引數和不可變引數
知識點 python中,萬物皆物件。python中不存在所謂的傳值呼叫,一切傳遞的都是物件的引用,也可以認為是傳址。python在heap中分配的物件分成兩類 可變物件和不可變物件。所謂可變物件是指,物件的內容可變,而不可變物件是指物件內容不可變。不可變 immutable int 字串 strin...
Python中的可變物件和不可變物件
python中,數值型別 int和float 字串str 元組tuple都是不可變型別。而列表list 字典dict 集合set是可變型別。還是看 比較直觀。先看不可變物件 先說明一點is就是判斷兩個物件的id是否相同,而 判斷的則是內容是否相同。a 2b 2c a 0 c 0 print id a...