昨晚有同學問了一個非常有意思的問題,問題本身很簡單,卻包含了初學者不易理解、編碼實踐中又處處可見的幾個知識點,如果對這些知識點理解有偏差,即便是經驗豐富的Python程式員,稍不留神也會掉進坑里,
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)
運行上面的代碼之后,下面哪一個結果是正確的呢?
- A. [5, 9, 2, 1]
- B. [5, 9, 2, 1, 5]
- C. [5, 9, 2, 1, 5, 9]
- D. [5, 9, 2, 1, 5, 9, 2]
- E. [5, 9, 2, 1, 5, 9, 2, 1]
- F. 以上都不對
如果將上面代碼稍作改動,函式中的賦值操作(=)變成加等操作(+=),正確的輸出結果又是哪一個呢?
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)
要想正確回答問題,首先要了解可變物件和不可變物件的概念,以及可變物件和不可變物件作為函式引數是如何向函式傳參的,
那么,什么是可變物件、什么是不可變物件呢?在Python中,整型(int)、浮點型(float)、布爾型(bool)、元組(tuple)和字串(str)等內置類,一旦實體化就不可改變,屬于不可變物件;而串列(list)、字典(dict)和集合(set)等內置類,實體化后得到物件,可以任意修改,
讀到這里,有些初學者可能會不理解:元組、字串不可改變,老師和教科書上都是這么說的,整型浮點型物件為什么不可變呢?讓x=1之后,x就不能改變了嗎?加1不就變成2了嗎?顯然,這是對物件概念的誤解,x=1,是讓x指向了值為1的整型物件,但x并不是真正的整型物件,只是一個名字而已,我們稱其為變數名或物件名 ,x加1變成2,并非值為1的整型物件自身加1,而是讓x指向了另一個值為2的整型物件,
>>> 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函式(回傳變數名所指物件的記憶體首地址),可以清楚看到,執行加等操作(+=)之后,x指向的整型物件地址發生了改變,y指向的串列物件地址并未改變,不過,對y執行賦值操作(=)之后,y指向的串列物件地址發生了改變,這表明,賦值操作(=)是在變數名和物件之間新建對應關系,而加等操作(+=)并不改變變數名和物件之間的對應關系,除非物件是不可變的,
理解了可變物件和不可變物件的概念之后,就很容易理解可變物件和不可變物件作為函式引數是如何向函式傳參的了,如下圖所示,紅色箭頭指向的是可變物件作為引數傳遞到函式,綠色箭頭指向的是不可變物件作為引數傳遞到函式,對于不可變物件而言,無論函式內部如何改變這些引數,都不會影響到函式外部的不可變物件,因為他們是不可改變的,對于可變物件來說,函式內部對于它們的任何操作都是施加于物件本身的,這個物件即函式外部的變數所指向的物件,需要說明的是,函式的引數傳遞,并不要求實際引數和形式引數同名,下圖紅綠箭頭對應的實際引數和形式引數名字相同,僅是我個人的習慣,并非規則要求,

是時候進入正題了,第一段代碼中,可變物件w和不可變物件a、b作為引數傳進函式后,內部變數名w和外部變數名w指向同一個物件,append操作自然也會影響外部變數名w所指向的串列物件,其后的賦值操作將函式內部的變數名w指向了另外一個新的串列物件,因而不會改變外部的變數名w所指向的串列物件,如下圖所示,不言自明,代碼最后的輸出結果應該是D,即[5, 9, 2, 1, 5, 9, 2],

在第二段代碼中,由于+=操作不改變內部變數w的指向,外邊變數w所指向的串列自然也變成了[5, 9, 2, 1],最終的輸出結果是兩個[5, 9, 2, 1]相加,正確答案是E, 如下圖所示,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/286962.html
標籤:python
