python 函数参数传递机制_Python函数参数传递机制(超级详细)
Python中,函數(shù)參數(shù)由實(shí)參傳遞給形參的過程,是由參數(shù)傳遞機(jī)制來控制的。通過學(xué)習(xí)《Python函數(shù)值傳遞和引用傳遞》一節(jié)我們知道,根據(jù)實(shí)際參數(shù)的類型不同,函數(shù)參數(shù)的傳遞方式分為值傳遞和引用傳遞(又稱為地址傳遞),本節(jié)將對(duì)這兩種傳遞機(jī)制做深度剖析。
Python函數(shù)參數(shù)的值傳遞機(jī)制
所謂值傳遞,實(shí)際上就是將實(shí)際參數(shù)值的副本(復(fù)制品)傳入函數(shù),而參數(shù)本身不會(huì)受到任何影響。
值傳遞的方式,類似于《西游記》里的孫悟空,它復(fù)制一個(gè)假孫悟空,假孫悟空具有的能力和真孫悟空相同,可除妖或被砍頭。但不管這個(gè)假孫悟空遇到什么事,真孫悟空都不會(huì)受到任何影響。與此類似,傳入函數(shù)的是實(shí)際參數(shù)值的復(fù)制品,不管在函數(shù)中對(duì)這個(gè)復(fù)制品如何操作,實(shí)際參數(shù)值本身不會(huì)受到任何影響。
下面程序演示了函數(shù)參數(shù)進(jìn)行值傳遞的效果:
def swap(a, b) :
# 下面代碼實(shí)現(xiàn)a、b變量的值交換
a, b = b, a
print("swap函數(shù)里,a的值是", a, ";b的值是", b)
a = 6
b = 9
swap(a , b)
print("交換結(jié)束后,變量a的值是", a, "; 變量b的值是", b)
運(yùn)行上面程序,將看到如下運(yùn)行結(jié)果:
swap函數(shù)里,a的值是 9 ;b的值是 6
交換結(jié)束后,變量a的值是 6 ;變量b的值是 9
從上面的運(yùn)行結(jié)果來看,在 swap() 函數(shù)里,a 和 b 的值分別是 9、6,交換結(jié)束后,變量 a 和 b 的值依然是 6、9。從這個(gè)運(yùn)行結(jié)果可以看出,程序中實(shí)際定義的變量 a 和 b,并不是 swap() 函數(shù)里的 a 和 b 。
正如前面所講的,swap() 函數(shù)里的 a 和 b 只是主程序中變量 a 和 b 的復(fù)制品。下面通過示意圖來說明上面程序的執(zhí)行過程。
上面程序開始定義了 a、b 兩個(gè)局部變量,這兩個(gè)變量在內(nèi)存中的存儲(chǔ)示意圖如圖 1 所示。
主棧區(qū)中 a、b 變量存儲(chǔ)示意圖
當(dāng)程序執(zhí)行 swap() 函數(shù)時(shí),系統(tǒng)進(jìn)入 swap() 函數(shù),并將主程序中的 a、b 變量作為參數(shù)值傳入 swap() 函數(shù),但傳入 swap() 函數(shù)的只是 a、b 的副本,而不是 a、b 本身。進(jìn)入 swap() 函數(shù)后,系統(tǒng)中產(chǎn)生了 4 個(gè)變量,這 4 個(gè)變量在內(nèi)存中的存儲(chǔ)示意圖如圖 2 所示。
主棧區(qū)的變量作為參數(shù)值傳入swap()函數(shù)后存儲(chǔ)示意圖
當(dāng)在主程序中調(diào)用 swap() 函數(shù)時(shí),系統(tǒng)分別為主程序和 swap() 函數(shù)分配兩塊棧區(qū),用于保存它們的局部變量。將主程序中的 a、b 變量作為參數(shù)值傳入 swap() 函數(shù),實(shí)際上是在 swap() 函數(shù)棧區(qū)中重新產(chǎn)生了兩個(gè)變量 a、b,并將主程序棧區(qū)中 a、b 變量的值分別賦值給 swap() 函數(shù)棧區(qū)中的 a、b 參數(shù)(就是對(duì) swap() 函數(shù)的 a、b 兩個(gè)變量進(jìn)行初始化)。此時(shí),系統(tǒng)存在兩個(gè) a 變量、兩個(gè) b 變量,只是存在于不同的棧區(qū)中而己。
程序在 swap() 函數(shù)中交換 a、b 兩個(gè)變量的值,實(shí)際上是對(duì)圖 2 中灰色區(qū)域的 a、b 變量進(jìn)行交換。交換結(jié)束后,輸出 swap() 函數(shù)中 a、b 變量的值,可以看到 a 的值為 9,b 的值為 6,此時(shí)在內(nèi)存中的存儲(chǔ)示意圖如圖 3 所示。
swap()函數(shù)中a、b 交換之后的存儲(chǔ)示意圖
對(duì)比圖 3 與圖 1,可以看到兩個(gè)示意圖中主程序棧區(qū)中 a、b 的值并未有任何改變,程序改變的只是 swap() 函數(shù)棧區(qū)中 a、b 的值。這就是值傳遞的實(shí)質(zhì):當(dāng)系統(tǒng)開始執(zhí)行函數(shù)時(shí),系統(tǒng)對(duì)形參執(zhí)行初始化,就是把實(shí)參變量的值賦給函數(shù)的形參變量,在函數(shù)中操作的并不是實(shí)際的實(shí)參變量。
Python函數(shù)參數(shù)的引用傳遞
如果實(shí)際參數(shù)的數(shù)據(jù)類型是可變對(duì)象(列表、字典),則函數(shù)參數(shù)的傳遞方式將采用引用傳遞方式。需要注意的是,引用傳遞方式的底層實(shí)現(xiàn),采用的依然還是值傳遞的方式。
下面程序示范了引用傳遞參數(shù)的效果:
def swap(dw):
# 下面代碼實(shí)現(xiàn)dw的a、b兩個(gè)元素的值交換
dw['a'], dw['b'] = dw['b'], dw['a']
print("swap函數(shù)里,a元素的值是",\
dw['a'], ";b元素的值是", dw['b'])
dw = {'a': 6, 'b': 9}
swap(dw)
print("交換結(jié)束后,a元素的值是",\
dw['a'], ";b元素的值是", dw['b'])
運(yùn)行上面程序,將看到如下運(yùn)行結(jié)果:
swap函數(shù)里,a元素的值是 9 ;b元素的值是 6
交換結(jié)束后,a元素的值是 9 ;b元素的值是 6
從上面的運(yùn)行結(jié)果來看,在 swap() 函數(shù)里,dw 字典的 a、b 兩個(gè)元素的值被交換成功。不僅如此,當(dāng) swap() 函數(shù)執(zhí)行結(jié)束后,主程序中 dw 字典的 a、b 兩個(gè)元素的值也被交換了。這很容易造成一種錯(cuò)覺,即在調(diào)用 swap() 函數(shù)時(shí),傳入 swap() 函數(shù)的就是 dw 字典本身,而不是它的復(fù)制品。但這只是一種錯(cuò)覺,下面還是結(jié)合示意圖來說明程序的執(zhí)行過程。
程序開始創(chuàng)建了一個(gè)字典對(duì)象,并定義了一個(gè) dw 引用變量(其實(shí)就是一個(gè)指針)指向字典對(duì)象,這意味著此時(shí)內(nèi)存中有兩個(gè)東西:對(duì)象本身和指向該對(duì)象的引用變量。此時(shí)在系統(tǒng)內(nèi)存中的存儲(chǔ)示意圖如圖 4 所示:
主程序創(chuàng)建了字典對(duì)象后存儲(chǔ)示意圖
接下來主程序開始調(diào)用 swap() 函數(shù),在調(diào)用 swap() 函數(shù)時(shí),dw 變量作為參數(shù)傳入 swap() 函數(shù),這里依然采用值傳遞方式:把主程序中 dw 變量的值賦給 swap() 函數(shù)的 dw 形參,從而完成 swap() 函數(shù)的 dw 參數(shù)的初始化。值得指出的是,主程序中的 dw 是一個(gè)引用變量(也就是一個(gè)指針),它保存了字典對(duì)象的地址值,當(dāng)把 dw 的值賦給 swap() 函數(shù)的 dw 參數(shù)后,就是讓 swap() 函數(shù)的 dw 參數(shù)也保存這個(gè)地址值,即也會(huì)引用到同一個(gè)字典對(duì)象。圖 5 顯示了 dw 字典傳入 swap() 函數(shù)后的存儲(chǔ)示意圖。
dw字典傳入swap()函數(shù)后存儲(chǔ)示意圖
從圖 5 來看,這種參數(shù)傳遞方式是不折不扣的值傳遞方式,系統(tǒng)一樣復(fù)制了dw 的副本傳入 swap() 函數(shù)。但由于 dw 只是一個(gè)引用變量,因此系統(tǒng)復(fù)制的是 dw 變量,并未復(fù)制字典本身。
當(dāng)程序在 swap() 函數(shù)中操作 dw 參數(shù)時(shí),由于 dw 只是一個(gè)引用變量,故實(shí)際操作的還是字典對(duì)象。此時(shí),不管是操作主程序中的 dw 變量,還是操作 swap() 函數(shù)里的 dw 參數(shù),其實(shí)操作的都是它們共同引用的字典對(duì)象,它們引用的是同一個(gè)字典對(duì)象。因此,當(dāng)在 swap() 函數(shù)中交換 dw 參數(shù)所引用字典對(duì)象的 a、b 兩個(gè)元素的值后,可以看到在主程序中 dw 變量所引用字典對(duì)象的 a、b 兩個(gè)元素的值也被交換了。
為了更好地證明主程序中的 dw 和 swap() 函數(shù)中的 dw 是兩個(gè)變量,在 swap() 函數(shù)的最后一行增加如下代碼:
把dw 直接賦值為None,讓它不再指向任何對(duì)象
dw = None
運(yùn)行上面代碼,結(jié)果是 swap() 函數(shù)中的 dw 變量不再指向任何對(duì)象,程序其他地方?jīng)]有任何改變。主程序調(diào)用 swap() 函數(shù)后,再次訪問 dw 變量的 a、b 兩個(gè)元素,依然可以輸出 9、6。可見,主程序中的 dw 變量沒有受到任何影響。實(shí)際上,當(dāng)在 swap() 函數(shù)中增加“dw =None”代碼后,在內(nèi)存中的存儲(chǔ)示意圖如圖 6 所示。
將swap()函數(shù)中的dw賦值為None 后存儲(chǔ)示意圖
從圖 6 來看,把 swap() 函數(shù)中的 dw 賦值為 None 后,在 swap() 函數(shù)中失去了對(duì)字典對(duì)象的引用,不可再訪問該字典對(duì)象。但主程序中的 dw 變量不受任何影響,依然可以引用該字典對(duì)象,所以依然可以輸出字典對(duì)象的 a、b 元素的值。
通過上面介紹可以得出如下兩個(gè)結(jié)論:
不管什么類型的參數(shù),在 Python 函數(shù)中對(duì)參數(shù)直接使用“=”符號(hào)賦值是沒用的,直接使用“=”符號(hào)賦值并不能改變參數(shù)。
如果需要讓函數(shù)修改某些數(shù)據(jù),則可以通過把這些數(shù)據(jù)包裝成列表、字典等可變對(duì)象,然后把列表、字典等可變對(duì)象作為參數(shù)傳入函數(shù),在函數(shù)中通過列表、字典的方法修改它們,這樣才能改變這些數(shù)據(jù)。
總結(jié)
以上是生活随笔為你收集整理的python 函数参数传递机制_Python函数参数传递机制(超级详细)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言实现单链表面试题汇总
- 下一篇: yiilite.php,YII Fram