Python3中函數(shù)參數(shù)傳遞方式實例詳解
本文實例講述了Python3中函數(shù)參數(shù)傳遞方式。分享給大家供大家參考,具體如下:
之前在看北理工嵩天等老師的python3的課程,在第五周中老師講到了函數(shù)的調(diào)用傳遞。老師講了這樣一個例子
#處理多個銀行賬戶的余額信息 def addInterest(balances, rate): for i in range(len(balances)): balances[i] = balances[i] * (1+rate) def test(): amounts = [1000, 105, 3500, 739] rate = 0.05 addInterest(amounts, rate) print(amounts) test()
在這個例子中可以看到為了處理多個銀行賬戶設(shè)置了amounts這個列表,老師的原話是這樣的:
“在test函數(shù)中一開始設(shè)置了amounts為4個值的列表,然后將amounts作為第一個參數(shù)傳遞給函數(shù)addInterest,并調(diào)用執(zhí)行該函數(shù),最后打印輸出amounts的結(jié)果,運行結(jié)果如下:
[1050.0,110.25,3675.0,775.95]
”
然后禮欣老師得出結(jié)論,以下為原話
“在這個例子中可以看到,amounts變量的值好像是被修改了,但是函數(shù)是不能改變變量本身即amounts的”
接下來是分析下函數(shù)的執(zhí)行過程,過程如下圖
分析原話如下
“接下來分析下整個執(zhí)行過程,查看amounts輸出結(jié)果是如何發(fā)生變化的。首先函數(shù)test的前兩條語句建立了兩個變量amounts和rate,然后控制傳遞給addinterest,這里amounts是一個包含4個整數(shù)類型值的列表對象,以實參的形式傳遞給函數(shù)addinterest形參balances,下一步執(zhí)行函數(shù)addinterest,從0到length-1范圍執(zhí)行循環(huán),并更新balances的值?!?/p>
重點來了:原話如下
“圖中舊值 [1000, 105, 3500, 739]
并沒有改變,只是Python又創(chuàng)建了一組新值[1050.0,110.25,3675.0,775.95]
,并且使列表對象指向該組新值,而舊值會在Python的垃圾數(shù)據(jù)回收的時候被清除掉③,從圖中我們可以清楚的看出,為什么包含列表參數(shù)的程序addinterest修改了列表的值?但程序addinterest結(jié)束時存儲在amounts中的是新balances的值,實際上變量amounts從來沒有被改變過?!?br /> “它(amounts,作者注)仍然指向的是調(diào)用addinterest函數(shù)之前的同一個列表,只是當控制返回到調(diào)用函數(shù)中時,列表呈現(xiàn)了被修改的狀態(tài)”②
最后是得出結(jié)論,原話如下:
“通過上述過程我們可以了解到:Python的參數(shù)是通過值來傳遞的。但是如果變量是可變對象,比如是列表或者是圖形對象,返回到調(diào)用程序后,該對象會呈現(xiàn)出被修改的狀態(tài)?!?/p>
^_^
注:課程原始視頻部分結(jié)束。
看了老師的這段講解之后產(chǎn)生了很多疑問:在前面(①處)講的amounts是不能被修改的,但是在(②處)又說列表呈現(xiàn)了被修改的狀態(tài),這不是自相矛盾嗎?在(③)處講列表創(chuàng)建了新值并且使列表指向了新值,這里不就是說amounts發(fā)生了改變嗎?怎么能說沒變呢?最后結(jié)論也是列表呈現(xiàn)出了被修改的狀態(tài)。這個結(jié)論云山霧繞,看得人似懂非懂。
那在Python3中參數(shù)變量是列表,在調(diào)用函數(shù)完返回后到底是被修改了還是沒被修改呢?
為了弄清這個問題,我做了一個實驗,id()可以查看變量在內(nèi)存中的地址,這個值相當于一個對象的“身份證”。
# 處理多個銀行賬戶的余額信息 def addInterest(balances, rates): print() print("第二處", id(balances)) for i in range(len(balances)): balances[i]= balances[i]*(1+rates) print() print("第三處",id(balances)) def test(): amounts = [1000,105,3500,739] print() print("第一處",id(amounts)) rate = 0.05 addInterest(amounts, rate) print() print(amounts) print() print("第四處",id(amounts)) test()
輸出結(jié)果:
第一處 41203656
第二處 41203656
第三處 41203656
第三處 41203656
第三處 41203656
第三處 41203656
[1050.0, 110.25, 3675.0, 775.95]
第四處 41203656
在這個實驗中可以清楚的看到,amounts這個對象的身份證號碼在整個程序運行過程中從未變過,而非視頻中老師講的創(chuàng)建了新的列表對象。所以amounts作為一個列表對象在程序運行過程中是被直接修改了,是的就是直接被修改了,而非指向新balances的值。為什么可以得出這一結(jié)論?我們可以看下第一、三處的id,在未進入函數(shù)之前id是41203656(第一處),進入函數(shù)之后對象id仍然未變,函數(shù)運行完返回之后對象id仍然未變!
所以結(jié)論應該這樣寫會比較清楚:
改變參數(shù)值值的函數(shù):
實參傳給形參時,python的參數(shù)是通過值來傳遞的;
如果變量是可變對象(如列表或者圖形對象),該對象會在函數(shù)中會被直接修改,返回到調(diào)用程序后也是被修改后的狀態(tài)。
那是不是Python3中函數(shù)都是像這種傳遞方式呢?我們對課程視頻中的另一個例子做一個簡單的修改。
# 計算單個銀行賬戶余額 def addinterest(balance, rate): print("第二處", id(balance)) newBalance = balance * (1 + rate) print() print("第三處", id(balance)) print() print("第四處", id(newBalance)) return newBalance def main(): amount = 1000 print("第一處", id(amount)) print() rate = 0.05 amount = addinterest(amount, rate) print() print("第五處", id(amount)) print() print(amount) print("第六處", id(amount)) main()
運行結(jié)果如下:
第一處 33533648
第二處 33533648
第三處 33533648
第四處 33563344
第五處 33563344
1050.0
第六處 33563344
不是說好的直接修改的嗎?怎么身份證又變了?其實這里的對象amount是個常數(shù),即為不可變對象,當在函數(shù)中要對對象進行處理時,由于對象不可變,只能新建一個新對象,然后return出新的對象了。
這個也就是目前網(wǎng)絡(luò)上大部分博客的結(jié)論:
1、不可變對象作為函數(shù)參數(shù),Python通過值傳遞;
2、 可變對象作為函數(shù)參數(shù),Python通過引用傳遞。
注:Python中,數(shù)值類型(int和float)、字符串str、元組tuple都是不可變類型。而列表list、字典dict、集合set是可變類型。
(但是也有博客把這兩個結(jié)論搞反了)
但是也有博客提出了一個類似這樣的例子
def change(val): val = val + [10] nums = [0, 1] change(nums) print(nums)
輸出結(jié)果為
[0, 1]
其實這里是寫的不嚴謹,不能直接用加號添加列表元素
可以改為這樣
def change(val): newval = [10] val= val + newval nums = [0, 1] change(nums) print(nums)
但是輸出結(jié)果還是
[0, 1]
難道上面的結(jié)論不對嗎?
其實這里要補充另外一種情況:對于可變對象作為函數(shù)參數(shù),且參數(shù)不指向其他對象時,相當于引用傳遞;否則,若參數(shù)指向其他對象,則對參數(shù)變量的操作并不影響原變量的對象值
函數(shù)里的參數(shù)變量val指向了與nums不同的內(nèi)存變量,所以函數(shù)里的參數(shù)變量val不影響原變量nums的值
**這也是因為python的特性” 變量無類型,對象有類型 “。
變量是對內(nèi)存空間的引用,當參數(shù)變量和原變量指向不同的內(nèi)存空間時,操作互不影響。**
用下面這個看下
def change(val): newval = [10] print("第二處",id(val)) val = val + newval print("第三處",id(val)) nums = [0, 1] print("第一處",id(nums)) change(nums) print("第四處",id(nums)) print(nums)
運行結(jié)果如下:
第一處 39695944
第二處 39695944
第三處 39710024
第四處 39695944
[0, 1]
可以看到第一處的nums和第二處的val的內(nèi)存地址完全一樣,然后執(zhí)行到第三處時,由于函數(shù)內(nèi)VAL重新指向了別的內(nèi)存變量,所以內(nèi)存地址不同。但是最后結(jié)果要輸出變量nums,即第一處第二處內(nèi)存地址的值,所以和第三處的val就沒關(guān)系了。其實這里的val是沒有返回值的。
想要直接在列表中添加元素可以寫成這樣:
def change(val): val.append(10) nums = [0, 1] change(nums) print(nums)
輸出結(jié)果是
[0, 1, 10]
關(guān)于變量無類型,對象有類型可以這樣理解:只有放在內(nèi)存空間中的對象(也就是數(shù)據(jù))才有類型,而變量是沒有類型的。
如果還是不明白可以做這樣一種比喻:變量就好比釣魚者,湖水就好像內(nèi)存,里面有各種各樣的魚,它們就是對象。釣魚者(變量)的任務(wù)就是用某種方式把自己和魚(對象)通過魚線連接起來。那么,魚(對象)是有類型的,有鰱魚、鯽魚、帶魚。釣魚者(變量)沒有類型,他釣到不同類型的魚(對象)。
用釣魚的比喻解釋下上面的例子
def change(val): newval = [10] val= val + newval nums = [0, 1] change(nums) print(nums)
1、釣魚人已經(jīng)釣了一桶魚用nums桶裝著,nums桶可以裝很多魚。
2、現(xiàn)在提著這個nums桶繼續(xù)在湖里釣魚,這時候nums桶暫時叫做裝魚桶val,突然釣魚人釣了一條大魚,發(fā)現(xiàn)裝魚桶val裝不下,于是釣魚人又在漁具店買了另一個大的裝魚桶VAL,把大魚和之前的魚一塊裝了。
釣魚活動結(jié)束。3、最后要看看那個叫nums的桶有哪些魚,這時候當然只能看之前的情況。
即這個結(jié)論:對于可變對象作為函數(shù)參數(shù),且參數(shù)不指向其他對象時,相當于引用傳遞;否則,若參數(shù)指向其他對象,則對參數(shù)變量的操作并不影響原變量的對象值。
同樣的針對其他兩個結(jié)論,也可以用這個比喻解釋:
def change( val): newval = val + 10 return newval num = 1 num = change(num) print(num)
1、釣魚人手上的東西num是個小蚯蚓。
2、釣魚人拿著num去湖邊釣魚,小蚯蚓被大魚吃了,釣魚人釣到了一條大魚,釣魚人拿著魚回家。
釣魚活動結(jié)束。3、問釣魚人手上現(xiàn)在拿著什么東西num?當然是一條大魚。
def change(val): val.append(10) nums = [0, 1] change(nums) print(nums)
1、釣魚人提著一個叫nums的桶,桶里裝著2條魚
2、釣魚人來到湖邊釣魚,此時桶暫時叫裝魚桶val,釣魚人釣到了一條魚放進裝魚桶val。
釣魚活動結(jié)束。3、看看釣魚人桶里的有幾條魚。
總結(jié)來說:
**對于不可變對象作為函數(shù)參數(shù),相當于C系語言的值傳遞;
對于可變對象作為函數(shù)參數(shù),且參數(shù)不指向其他對象時,相當于C系語言的引用傳遞。
對于可變對象作為函數(shù)參數(shù),參數(shù)指向其他對象,對參數(shù)變量的操作不影響原變量的值。**
關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python函數(shù)使用技巧總結(jié)》、《Python面向?qū)ο蟪绦蛟O(shè)計入門與進階教程》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python字符串操作技巧匯總》、《Python編碼操作技巧總結(jié)》及《Python入門與進階經(jīng)典教程》
希望本文所述對大家Python程序設(shè)計有所幫助。
相關(guān)文章
Selenium webdriver添加cookie實現(xiàn)過程詳解
這篇文章主要介紹了Selenium webdriver添加cookie實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08pycharm部署django項目到云服務(wù)器的詳細流程
今天重點給大家介紹pycharm部署django項目到云服務(wù)器的詳細流程,首先大家需要先下載python3.8壓縮包,然后通過一系列命令完成操作,具體實現(xiàn)方法,跟隨小編一起看看吧2021-06-06Python實現(xiàn)監(jiān)視程序的內(nèi)存使用情況
我們使用Python和它的數(shù)據(jù)處理庫套件進行大量數(shù)據(jù)處理時候,可能使用了大量的計算資源,那么如何監(jiān)視程序的內(nèi)存使用情況就顯得尤為重要,下面我們就來了解一下具體實現(xiàn)方法吧2023-12-12springboot aop方式實現(xiàn)接口入?yún)⑿r灥氖纠a
在實際開發(fā)項目中,我們常常需要對接口入?yún)⑦M行校驗,本文主要介紹了springboot aop方式實現(xiàn)接口入?yún)⑿r灥氖纠a,具有一定的參考價值,感興趣的可以了解一下2023-08-08教你兩步解決conda安裝pytorch時下載速度慢or超時的問題
這篇文章主要介紹了教你兩步解決conda安裝pytorch時下載速度慢or超時的問題,使用清華鏡像源可以大大減少安裝的時間,需要的朋友可以參考下2023-03-03Python將腳本程序轉(zhuǎn)變?yōu)榭蓤?zhí)行程序的實現(xiàn)
本文主要介紹了Python將腳本程序轉(zhuǎn)變?yōu)榭蓤?zhí)行程序的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-02-02