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