淺談Python3中函數(shù)參數(shù)傳遞方式

淺談Python3中函數(shù)參數(shù)傳遞方式

之前在看北理工嵩天等老師的python3的課程煮纵,在第五周中老師講到了函數(shù)的調(diào)用傳遞勇皇。老師講了這樣一個(gè)例子

#處理多個(gè)銀行賬戶的余額信息
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()  

在這個(gè)例子中可以看到為了處理多個(gè)銀行賬戶設(shè)置了amounts這個(gè)列表劲赠,老師的原話是這樣的:

“在test函數(shù)中一開始設(shè)置了amounts為4個(gè)值的列表切距,然后將amounts作為第一個(gè)參數(shù)傳遞給函數(shù)addInterest,并調(diào)用執(zhí)行該函數(shù)啥供,最后打印輸出amounts的結(jié)果忘伞,運(yùn)行結(jié)果如下:

[1050.0,110.25微峰,3675.0舷丹,775.95] 

然后禮欣老師得出結(jié)論,以下為原話

在這個(gè)例子中可以看到蜓肆,amounts變量的值好像是被修改了颜凯,但是函數(shù)是不能改變變量本身即amounts的

接下來(lái)是分析下函數(shù)的執(zhí)行過(guò)程,過(guò)程如下圖


addinterest()函數(shù).png-267.9kB
addinterest()函數(shù).png-267.9kB

分析原話如下

“接下來(lái)分析下整個(gè)執(zhí)行過(guò)程仗扬,查看amounts輸出結(jié)果是如何發(fā)生變化的症概。首先函數(shù)test的前兩條語(yǔ)句建立了兩個(gè)變量amounts和rate,然后控制傳遞給addinterest,這里amounts是一個(gè)包含4個(gè)整數(shù)類型值的列表對(duì)象早芭,以實(shí)參的形式傳遞給函數(shù)addinterest形參balances,下一步執(zhí)行函數(shù)addinterest彼城,從0到length-1范圍執(zhí)行循環(huán),并更新balances的值逼友。”

重點(diǎn)來(lái)了:原話如下

“圖中舊值 [1000, 105, 3500, 739]
并沒有改變秤涩,只是Python又創(chuàng)建了一組新值[1050.0帜乞,110.25,3675.0筐眷,775.95]
,并且使列表對(duì)象指向該組新值黎烈,而舊值會(huì)在Python的垃圾數(shù)據(jù)回收的時(shí)候被清除掉③,從圖中我們可以清楚的看出匀谣,為什么包含列表參數(shù)的程序addinterest修改了列表的值照棋?但程序addinterest結(jié)束時(shí)存儲(chǔ)在amounts中的是新balances的值,實(shí)際上變量amounts從來(lái)沒有被改變過(guò)武翎。

它(amounts烈炭,作者注)仍然指向的是調(diào)用addinterest函數(shù)之前的同一個(gè)列表,只是當(dāng)控制返回到調(diào)用函數(shù)中時(shí)宝恶,列表呈現(xiàn)了被修改的狀態(tài)”②

最后是得出結(jié)論符隙,原話如下:

"通過(guò)上述過(guò)程我們可以了解到:Python的參數(shù)是通過(guò)值來(lái)傳遞的。但是如果變量是可變對(duì)象垫毙,比如是列表或者是圖形對(duì)象霹疫,返回到調(diào)用程序后,該對(duì)象會(huì)呈現(xiàn)出被修改的狀態(tài)综芥。"

_
注:課程原始視頻部分結(jié)束丽蝎。

看了老師的這段講解之后產(chǎn)生了很多疑問:在前面(①處)講的amounts是不能被修改的,但是在(②處)又說(shuō)列表呈現(xiàn)了被修改的狀態(tài),這不是自相矛盾嗎?在(③)處講列表創(chuàng)建了新值并且使列表指向了新值,這里不就是說(shuō)amounts發(fā)生了改變嗎膀藐?怎么能說(shuō)沒變呢屠阻?最后結(jié)論也是列表呈現(xiàn)出了被修改的狀態(tài)红省。這個(gè)結(jié)論云山霧繞,看得人似懂非懂栏笆。

那在Python3中參數(shù)變量是列表类腮,在調(diào)用函數(shù)完返回后到底是被修改了還是沒被修改呢?

為了弄清這個(gè)問題蛉加,我做了一個(gè)實(shí)驗(yàn)蚜枢,id()可以查看變量在內(nèi)存中的地址,這個(gè)值相當(dāng)于一個(gè)對(duì)象的“身份證”针饥。

# 處理多個(gè)銀行賬戶的余額信息
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

在這個(gè)實(shí)驗(yàn)中可以清楚的看到厂抽,amounts這個(gè)對(duì)象的身份證號(hào)碼在整個(gè)程序運(yùn)行過(guò)程中從未變過(guò),而非視頻中老師講的創(chuàng)建了新的列表對(duì)象丁眼。所以amounts作為一個(gè)列表對(duì)象在程序運(yùn)行過(guò)程中是被直接修改了筷凤,是的就是直接被修改了,而非指向新balances的值苞七。為什么可以得出這一結(jié)論藐守?我們可以看下第一、三處的id蹂风,在未進(jìn)入函數(shù)之前id是41203656(第一處)卢厂,進(jìn)入函數(shù)之后對(duì)象id仍然未變,函數(shù)運(yùn)行完返回之后對(duì)象id仍然未變惠啄!

所以結(jié)論應(yīng)該這樣寫會(huì)比較清楚:

改變參數(shù)值值的函數(shù):
實(shí)參傳給形參時(shí)慎恒,python的參數(shù)是通過(guò)值來(lái)傳遞的;
如果變量是可變對(duì)象(如列表或者圖形對(duì)象)撵渡,該對(duì)象會(huì)在函數(shù)中會(huì)被直接修改融柬,返回到調(diào)用程序后也是被修改后的狀態(tài)。

那是不是Python3中函數(shù)都是像這種傳遞方式呢趋距?我們對(duì)課程視頻中的另一個(gè)例子做一個(gè)簡(jiǎn)單的修改粒氧。

 # 計(jì)算單個(gè)銀行賬戶余額
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()

運(yùn)行結(jié)果如下:

第一處 33533648

第二處 33533648

第三處 33533648

第四處 33563344

第五處 33563344

1050.0
第六處 33563344

不是說(shuō)好的直接修改的嗎?怎么身份證又變了节腐?其實(shí)這里的對(duì)象amount是個(gè)常數(shù)靠欢,即為不可變對(duì)象,當(dāng)在函數(shù)中要對(duì)對(duì)象進(jìn)行處理時(shí)铜跑,由于對(duì)象不可變门怪,只能新建一個(gè)新對(duì)象,然后return出新的對(duì)象了锅纺。

這個(gè)也就是目前網(wǎng)絡(luò)上大部分博客的結(jié)論:
1掷空、不可變對(duì)象作為函數(shù)參數(shù),Python通過(guò)值傳遞;
2坦弟、 可變對(duì)象作為函數(shù)參數(shù)护锤,Python通過(guò)引用傳遞。
注:Python中酿傍,數(shù)值類型(int和float)烙懦、字符串str、元組tuple都是不可變類型赤炒。而列表list氯析、字典dict、集合set是可變類型莺褒。
(但是也有博客把這兩個(gè)結(jié)論搞反了)

但是也有博客提出了一個(gè)類似這樣的例子

def change(val):
    val = val + [10]
nums = [0, 1]
change(nums)
print(nums)

輸出結(jié)果為

[0, 1]

其實(shí)這里是寫的不嚴(yán)謹(jǐn)掩缓,不能直接用加號(hào)添加列表元素
可以改為這樣

def change(val):
    newval = [10]
    val= val + newval
    
nums = [0, 1]
change(nums)
print(nums)

但是輸出結(jié)果還是

[0, 1]

難道上面的結(jié)論不對(duì)嗎?
其實(shí)這里要補(bǔ)充另外一種情況:對(duì)于可變對(duì)象作為函數(shù)參數(shù)遵岩,且參數(shù)不指向其他對(duì)象時(shí)你辣,相當(dāng)于引用傳遞;否則尘执,若參數(shù)指向其他對(duì)象舍哄,則對(duì)參數(shù)變量的操作并不影響原變量的對(duì)象值

函數(shù)里的參數(shù)變量val指向了與nums不同的內(nèi)存變量,所以函數(shù)里的參數(shù)變量val不影響原變量nums的值

3.png-23.8kB
3.png-23.8kB

這也是因?yàn)閜ython的特性" 變量無(wú)類型,對(duì)象有類型 "誊锭。
變量是對(duì)內(nèi)存空間的引用表悬,當(dāng)參數(shù)變量和原變量指向不同的內(nèi)存空間時(shí),操作互不影響炉旷。

用下面這個(gè)看下

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)

運(yùn)行結(jié)果如下:

第一處 39695944
第二處 39695944
第三處 39710024
第四處 39695944
[0, 1]

可以看到第一處的nums和第二處的val的內(nèi)存地址完全一樣签孔,然后執(zhí)行到第三處時(shí)叉讥,由于函數(shù)內(nèi)VAL重新指向了別的內(nèi)存變量窘行,所以內(nèi)存地址不同。但是最后結(jié)果要輸出變量nums图仓,即第一處第二處內(nèi)存地址的值罐盔,所以和第三處的val就沒關(guān)系了。其實(shí)這里的val是沒有返回值的救崔。

想要直接在列表中添加元素可以寫成這樣:

def change(val):
    val.append(10)
nums = [0, 1]
change(nums)
print(nums)

輸出結(jié)果是

[0, 1, 10]

關(guān)于變量無(wú)類型惶看,對(duì)象有類型可以這樣理解:只有放在內(nèi)存空間中的對(duì)象(也就是數(shù)據(jù))才有類型,而變量是沒有類型的六孵。

如果還是不明白可以做這樣一種比喻:變量就好比釣魚者纬黎,湖水就好像內(nèi)存,里面有各種各樣的魚劫窒,它們就是對(duì)象本今。釣魚者(變量)的任務(wù)就是用某種方式把自己和魚(對(duì)象)通過(guò)魚線連接起來(lái)。那么,魚(對(duì)象)是有類型的冠息,有鰱魚挪凑、鯽魚、帶魚逛艰。釣魚者(變量)沒有類型躏碳,他釣到不同類型的魚(對(duì)象)。

用釣魚的比喻解釋下上面的例子

def change(val):
    newval = [10]
    val= val + newval
    
nums = [0, 1]
change(nums)
print(nums)

1散怖、釣魚人已經(jīng)釣了一桶魚用nums桶裝著菇绵,nums桶可以裝很多魚。

2杭抠、現(xiàn)在提著這個(gè)nums桶繼續(xù)在湖里釣魚脸甘,這時(shí)候nums桶暫時(shí)叫做裝魚桶val,突然釣魚人釣了一條大魚,發(fā)現(xiàn)裝魚桶val裝不下偏灿,于是釣魚人又在漁具店買了另一個(gè)大的裝魚桶VAL丹诀,把大魚和之前的魚一塊裝了。

釣魚活動(dòng)結(jié)束翁垂。
3铆遭、最后要看看那個(gè)叫nums的桶有哪些魚,這時(shí)候當(dāng)然只能看之前的情況沿猜。

即這個(gè)結(jié)論:對(duì)于可變對(duì)象作為函數(shù)參數(shù)枚荣,且參數(shù)不指向其他對(duì)象時(shí),相當(dāng)于引用傳遞啼肩;否則橄妆,若參數(shù)指向其他對(duì)象,則對(duì)參數(shù)變量的操作并不影響原變量的對(duì)象值祈坠。

同樣的針對(duì)其他兩個(gè)結(jié)論害碾,也可以用這個(gè)比喻解釋:

def change( val):
    newval = val + 10
    return  newval
num = 1
num = change(num)
print(num)
1、釣魚人手上的東西num是個(gè)小蚯蚓赦拘。
2慌随、釣魚人拿著num去湖邊釣魚,小蚯蚓被大魚吃了躺同,釣魚人釣到了一條大魚阁猜,釣魚人拿著魚回家。
釣魚活動(dòng)結(jié)束蹋艺。
3剃袍、問釣魚人手上現(xiàn)在拿著什么東西num?當(dāng)然是一條大魚捎谨。
def change(val):
    val.append(10)
nums = [0, 1]
change(nums)
print(nums)

1民效、釣魚人提著一個(gè)叫nums的桶隘击,桶里裝著2條魚 2、釣魚人來(lái)到湖邊釣魚研铆,此時(shí)桶暫時(shí)叫裝魚桶val埋同,釣魚人釣到了一條魚放進(jìn)裝魚桶val。
釣魚活動(dòng)結(jié)束棵红。
3凶赁、看看釣魚人桶里的有幾條魚。

總結(jié)來(lái)說(shuō):

對(duì)于不可變對(duì)象作為函數(shù)參數(shù)逆甜,相當(dāng)于C系語(yǔ)言的值傳遞虱肄;
對(duì)于可變對(duì)象作為函數(shù)參數(shù),且參數(shù)不指向其他對(duì)象時(shí)交煞,相當(dāng)于C系語(yǔ)言的引用傳遞咏窿。
對(duì)于可變對(duì)象作為函數(shù)參數(shù),參數(shù)指向其他對(duì)象素征,對(duì)參數(shù)變量的操作不影響原變量的值集嵌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市御毅,隨后出現(xiàn)的幾起案子根欧,更是在濱河造成了極大的恐慌,老刑警劉巖端蛆,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤粗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡今豆,警方通過(guò)查閱死者的電腦和手機(jī)嫌拣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)呆躲,“玉大人异逐,你說(shuō)我怎么就攤上這事〖呋啵” “怎么了应役?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵情组,是天一觀的道長(zhǎng)燥筷。 經(jīng)常有香客問我,道長(zhǎng)院崇,這世上最難降的妖魔是什么肆氓? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮底瓣,結(jié)果婚禮上谢揪,老公的妹妹穿的比我還像新娘蕉陋。我一直安慰自己,他們只是感情好拨扶,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布凳鬓。 她就那樣靜靜地躺著,像睡著了一般患民。 火紅的嫁衣襯著肌膚如雪缩举。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天匹颤,我揣著相機(jī)與錄音仅孩,去河邊找鬼。 笑死印蓖,一個(gè)胖子當(dāng)著我的面吹牛辽慕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赦肃,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼溅蛉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了他宛?” 一聲冷哼從身側(cè)響起温艇,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堕汞,沒想到半個(gè)月后勺爱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讯检,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年琐鲁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片人灼。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡围段,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出投放,到底是詐尸還是另有隱情奈泪,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布灸芳,位于F島的核電站涝桅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏烙样。R本人自食惡果不足惜冯遂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谒获。 院中可真熱鬧蛤肌,春花似錦壁却、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至炒俱,卻和暖如春琅锻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背向胡。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工恼蓬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人僵芹。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓处硬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拇派。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荷辕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容