一個(gè)看似初級(jí)的Python小問題,高手也難保不掉進(jìn)坑里
本文轉(zhuǎn)載自微信公眾號(hào)「Python作業(yè)輔導(dǎo)員」,作者天元浪子 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Python作業(yè)輔導(dǎo)員公眾號(hào)。
昨晚有同學(xué)問了一個(gè)非常有意思的問題。問題本身很簡(jiǎn)單,卻包含了初學(xué)者不易理解、編碼實(shí)踐中又處處可見的幾個(gè)知識(shí)點(diǎn)。如果對(duì)這些知識(shí)點(diǎn)理解有偏差,即便是經(jīng)驗(yàn)豐富的Python程序員,稍不留神也會(huì)掉進(jìn)坑里。
- def f(w, a, b):
- w.append(a)
- w = w + [b]
- return w
- w, a, b = [5, 9], 2, 1
- w = f(w, a, b) + w
- print(w)
如果將上面代碼稍作改動(dòng),函數(shù)中的賦值操作(=)變成加等操作(+=),正確的輸出結(jié)果又是哪一個(gè)呢?
- def f(w, a, b):
- w.append(a)
- w += [b]
- return w
- w, a, b = [5, 9], 2, 1
- w = f(w, a, b) + w
- print(w)
要想正確回答問題,首先要了解可變對(duì)象和不可變對(duì)象的概念,以及可變對(duì)象和不可變對(duì)象作為函數(shù)參數(shù)是如何向函數(shù)傳參的。
那么,什么是可變對(duì)象、什么是不可變對(duì)象呢?在Python中,整型(int)、浮點(diǎn)型(float)、布爾型(bool)、元組(tuple)和字符串(str)等內(nèi)置類,一旦實(shí)例化就不可改變,屬于不可變對(duì)象;而列表(list)、字典(dict)和集合(set)等內(nèi)置類,實(shí)例化后得到對(duì)象,可以任意修改。
讀到這里,有些初學(xué)者可能會(huì)不理解:元組、字符串不可改變,老師和教科書上都是這么說的,整型浮點(diǎn)型對(duì)象為什么不可變呢?讓x=1之后,x就不能改變了嗎?加1不就變成2了嗎?顯然,這是對(duì)對(duì)象概念的誤解。x=1,是讓x指向了值為1的整型對(duì)象,但x并不是真正的整型對(duì)象,只是一個(gè)名字而已,我們稱其為變量名或?qū)ο竺? 。x加1變成2,并非值為1的整型對(duì)象自身加1,而是讓x指向了另一個(gè)值為2的整型對(duì)象。
- >>> id(x)
- 2794729897296
- >>> x += 1
- >>> id(x)
- 2794730437616
- >>> y = [1,2]
- >>> id(y)
- 2794699482248
- >>> y += [3]
- >>> id(y)
- 2794699482248
- >>> y = y + [4]
- >>> id(y)
- 2794699482376
借助Python的id函數(shù)(返回變量名所指對(duì)象的內(nèi)存首地址),可以清楚看到,執(zhí)行加等操作(+=)之后,x指向的整型對(duì)象地址發(fā)生了改變,y指向的列表對(duì)象地址并未改變。不過,對(duì)y執(zhí)行賦值操作(=)之后,y指向的列表對(duì)象地址發(fā)生了改變。這表明,賦值操作(=)是在變量名和對(duì)象之間新建對(duì)應(yīng)關(guān)系,而加等操作(+=)并不改變變量名和對(duì)象之間的對(duì)應(yīng)關(guān)系,除非對(duì)象是不可變的。
理解了可變對(duì)象和不可變對(duì)象的概念之后,就很容易理解可變對(duì)象和不可變對(duì)象作為函數(shù)參數(shù)是如何向函數(shù)傳參的了。如下圖所示,紅色箭頭指向的是可變對(duì)象作為參數(shù)傳遞到函數(shù),綠色箭頭指向的是不可變對(duì)象作為參數(shù)傳遞到函數(shù)。對(duì)于不可變對(duì)象而言,無論函數(shù)內(nèi)部如何改變這些參數(shù),都不會(huì)影響到函數(shù)外部的不可變對(duì)象,因?yàn)樗麄兪遣豢筛淖兊摹?duì)于可變對(duì)象來說,函數(shù)內(nèi)部對(duì)于它們的任何操作都是施加于對(duì)象本身的,這個(gè)對(duì)象即函數(shù)外部的變量所指向的對(duì)象。需要說明的是,函數(shù)的參數(shù)傳遞,并不要求實(shí)際參數(shù)和形式參數(shù)同名,下圖紅綠箭頭對(duì)應(yīng)的實(shí)際參數(shù)和形式參數(shù)名字相同,僅是我個(gè)人的習(xí)慣,并非規(guī)則要求。
是時(shí)候進(jìn)入正題了。第一段代碼中,可變對(duì)象w和不可變對(duì)象a、b作為參數(shù)傳進(jìn)函數(shù)后,內(nèi)部變量名w和外部變量名w指向同一個(gè)對(duì)象,append操作自然也會(huì)影響外部變量名w所指向的列表對(duì)象。其后的賦值操作將函數(shù)內(nèi)部的變量名w指向了另外一個(gè)新的列表對(duì)象,因而不會(huì)改變外部的變量名w所指向的列表對(duì)象。如下圖所示,不言自明,代碼最后的輸出結(jié)果應(yīng)該是D,即[5, 9, 2, 1, 5, 9, 2]。
在第二段代碼中,由于+=操作不改變內(nèi)部變量w的指向,外邊變量w所指向的列表自然也變成了[5, 9, 2, 1],最終的輸出結(jié)果是兩個(gè)[5, 9, 2, 1]相加,正確答案是E, 如下圖所示。