Python中的切片拷貝和可變引數傳參

2022-09-21 17:57:11 字數 2765 閱讀 9130

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...