Python關于實參隨形參改變而改變的問題
前言
今天在實驗過程中,發(fā)現將字典作為函數的形參傳入函數,在函數內改變形參,會導致傳入的字典的值也發(fā)生相應的改變。
這與c++不同,令我疑惑,遂寫此文。
簡單實驗
我們對常見的數據類型進行實驗,檢測形參的該表是否會改變傳入的實參。
變量
def change(a):
a = 2
print(a)
b = 1
change(b)
print(b)
>>2
>>1可見,變量的值并沒有隨形參的改變而改變
元組
def change(a):
a = a[:2]
print(a)
b = (1, 2, 3)
change(b)
print(b)
>>(1, 2)
>>(1, 2, 3)可見,元組沒有隨形參的改變而改變
列表
def change(a):
a.append(1)
print(a)
b = [2, 3]
change(b)
print(b)
>>[2, 3, 1]
>>[2, 3, 1]可見,列表隨著形參的改變而發(fā)生改變
字典
def change(a):
a[1] = 100
print(a)
b = {1: 1, 2: 2}
change(b)
print(b)
>>{1: 100, 2: 2}
>>{1: 100, 2: 2}可見,字典隨著形參的改變而發(fā)生改變
原因
我們遇到了可變和不可變數據類型之間的差異。在Python中,數據類型可以是可變的,也可以是不可變的。
我們通常使用的(整數,浮點數,字符串,布爾值和元組)數據類型都是不可變的,但是列表和字典是可變的。
這意味著全局列表或字典即使在函數內部使用時也可以更改,我們通過上面的例子也能看出這個問題。
不可變數據舉例
a = 1
變量a 的作用類似于一個指向 1 的指針。
變量的數據類型是不可變的,變量一旦創(chuàng)建就不能別改變。
如果我們執(zhí)行 a = a + 1。
我們實際上不是將 1 更新到 2,而是指針從 1 指向了 2。
可變數據距離
list1 = [1, 2, 3]
如果我們在列表的末尾添加一個值,我們不是將list1指向另一個列表,而是直接更新現有列表。
如果我們創(chuàng)建多個列表變量,只要他們指向同一個列表,那么當列表發(fā)生改變時,這些列表變量都會發(fā)生變化。
list1 = [1, 2, 3] list2 = list1 list1.append(4) print(list1) print(list2) >>[1, 2, 3, 4] >>[1, 2, 3, 4]
心得
從這兩個例子中,我們可以直觀感受到可變數據類型與不可變數據類型之間的區(qū)別。
我們對可變數據的操作,是直接在其本身上進行操作的。
對不可變數據的操作,是將指針指向另一個位置,而不是更改其本身。
保持可變數據不變
我們在寫代碼時,經常會編寫各種函數。我們不希望傳入函數的實參,會在函數內部被改變。那應該怎么辦呢?
其實很簡單,使用.copy()方法復制列表或字典即可。
list1 = [1, 2, 3] list2 = list1.copy() list1.append(4) print(list1) print(list2) >>[1, 2, 3, 4] >>[1, 2, 3]
.copy()方法會創(chuàng)建一個新的副本,這樣list2就不會指向list1指向的列表,而是指向一個新的列表。
這樣的話,list1與list2就相互獨立,互不影響了。
def change(a):
temp = a.copy()
temp.append(4)
print(temp)
list1 = [1, 2, 3]
change(list1)
print(list1)
>>[1, 2, 3, 4]
>>[1, 2, 3]可見,這樣我們就解決了形參的改變帶來的實參改變的問題。對于字典也是一樣的。
補充
深拷貝與淺拷貝
>>> import copy >>> origin = [1, 2, [3, 4]] #origin 里邊有三個元素:1, 2,[3, 4] >>> cop1 = copy.copy(origin) >>> cop2 = copy.deepcopy(origin) >>> cop1 == cop2 # 判斷 cop1 和 cop2 的值是否相同 True >>> cop1 is cop2 # 判斷 cop1 和 cop2 是否是同一個對象 False #cop1 和 cop2 看上去相同,但已不再是同一個object >>> origin[2][0] = "hey!" >>> origin [1, 2, ['hey!', 4]] >>> cop1 [1, 2, ['hey!', 4]] >>> cop2 [1, 2, [3, 4]]
copy對于一個復雜對象的子對象并不會完全復制。什么是復雜對象的子對象呢?
比如列表里的嵌套列表(多維列表),字典里的嵌套字典(多維字典)都是復雜對象的子對象。
對于子對象,python會將其當做一個公共鏡像存儲起來,所有對它的復制都會被當成引用(就是拿指針指向同一塊區(qū)域)。所以,其中一個引用的值發(fā)生改變時,其他引用的值也會發(fā)生改變。
復制復雜對象,我們也想全盤復制(包括子對象),這時我們就可以使用deepcopy()函數進行深拷貝
import copy a = [1, 2, [3, 4]] b = copy.deepcopy(a) # 深拷貝
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
PyQt5實現將Matplotlib圖像嵌入到Scoll Area中顯示滾動條效果
我想知道是否有一種方法可以在matplotlib上顯示滾動條(水平或垂直),顯示包含多個子槽(sublot2grid)的頁面(plt.show).下面就通過本文給大家分享PyQt5實現將Matplotlib圖像嵌入到Scoll Area中顯示滾動條效果,對PyQt5 Matplotlib圖像嵌入相關知識感興趣的的朋友一起看看吧2021-05-05
Python的Django應用程序解決AJAX跨域訪問問題的方法
針對Django中在編寫供AJAX調用的API時碰到的跨域問題,我們來總結下Python的Django應用程序解決AJAX跨域訪問問題的方法,其中使用GitHub上開源分享的django-cors-headers尤其推薦2016-05-05
解決pytorch load huge dataset(大數據加載)
這篇文章主要介紹了解決pytorch load huge dataset(大數據加載)的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-05-05
Python調用win10toast框架實現定時調起系統(tǒng)通知
win10toast是一個windows通知的出發(fā)框架,使用它可以輕松的調起系統(tǒng)通知。通過它可以很方便的做一個定時通知的功能應用。本文將調用win10toast實現定時調起系統(tǒng)通知功能,需要的可以參考一下2022-01-01

