2014-09-25 66 views
0

如果我使用列表作爲函數的參數,我希望原始列表不會被修改。爲什麼當我使用下面的代碼時,x == z是真的呢應該是x == range(10)是真的嗎?爲什麼Python在這個函數中修改源代碼?

In [83]: def pop_three(collection): 
    ....:  new_collection = [] 
    ....:  new_collection.append(collection.pop()) 
    ....:  new_collection.append(collection.pop()) 
    ....:  new_collection.append(collection.pop()) 
    ....:  return new_collection, collection 
    ....: 

In [84]: x = range(10) 

In [85]: x 
Out[85]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

In [86]: y, z = pop_three(x) 

In [87]: y 
Out[87]: [9, 8, 7] 

In [88]: z 
Out[88]: [0, 1, 2, 3, 4, 5, 6] 

In [89]: x 
Out[89]: [0, 1, 2, 3, 4, 5, 6] 
+0

看到的頁面,http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference – han058 2014-09-25 00:23:42

回答

3

如果我用一個列表作爲參數的函數,我希望原來的列表是不變。

不,我認爲你在這裏失蹤的是,Python從未自動複製任何東西

如果你使用的語言一樣,比方說,C++,Python是非常不同的。 Ned Batchelder一如既往出色解釋這一點,但讓我總結:

  • 在C++中,一個功能參數,或變量,或數組元素,是在存儲器中的位置,與類型。當你寫a = b,或致電f(b),那份記憶位置b存儲位置a(可能調用轉換運算符或轉換構造函數或拷貝構造函數或複製這些的一半的賦值操作符,或移動版本的價值......但我們不是在這裏瞭解C++的複雜性)。
  • 在Python中,函數參數或變量或列表元素只是一個名稱。該值具有其自己的鍵入的內存位置,在別的地方。您可以爲同一對象設置儘可能多的名稱。當你寫a = b或致電f(b),這只是爲同一個對象的另一個名稱。

特別是,在你的情況下,collectionx是同一個對象的名稱。如果在呼叫前添加print(id(collection)),在功能內添加print(id(x)),則它們都會打印出相同的號碼。


因此,在Python中,如果你想要一個副本,你要問它明確 - 如,pop_three(x[:])pop_three(copy.copy(x))

或者,當然,你可以在pop_three以內做到這一點。

或者說,最重要的是,你可能只是避免使用變異的方法,如pop擺在首位:

def pop_three(collection): 
    return collection[-3:][::-1], collection[:-3] 

因爲傳遞給函數的字符串或整數可以治療我很困惑以這種方式。

那麼,他們可以按照這種方式完全按照列表​​處理。如果你從不改變任何東西,單獨拷貝和共享引用的區別是不相關的。*因爲Python字符串和整數是不可變的,所以區分不能出現在任何帶有字符串的代碼中,或者整數是參數。另一方面,列表是可變的,所以區別可以產生 - 但是如上面的最後一個例子那樣,編寫使用它們的代碼而不會突變任何東西是完全可能的,事實上最好的做法是最好的。

注意,這假設了上面的第一點:與C++賦值不同,Python賦值不是一種突變形式。但它的邊緣可能會變得棘手。例如,x[0] = 1是否爲突變?那麼,它不是x[0]的變異,但它是x的變異。那麼x += y?該語言留下了x的類型,對於可變類型,它通常(但並非總是)是一種變異,而對於不可變類型當然不是。

*這不完全正確;你可以編寫依賴於身份的代碼,即使它不相關,例如通過使用id函數或is運算符。這樣的代碼幾乎不會是Pythonic,或者一個好主意......但它可以幫助您瞭解事情的工作方式。以x = "a" + "b"嘗試print(id(x))建議。然而,這可能會引起誤解,因爲Python解釋器被允許「實習」它知道確保是不可變的對象,並且它可以用小整數這樣做,並且允許Python編譯器摺疊常量,並且它用例如文字字符串這樣做。

+0

我很困惑,因爲字符串或整數傳遞到一個函數*可以*以這種方式來對待。謝謝。 – 2014-09-25 04:08:21

+0

@profesor_tortuga:不,他們不能。字符串和整數是不變的。你 – Matthias 2014-09-25 08:08:16

+0

@profesor_tortuga:我已經更新了答案,試圖更好地解釋那個部分,因爲我認爲寫作它假設你會自己弄清楚 - 你顯然能夠做到這一點,但那可能不是對於同一問題的未來提問者也是如此。 – abarnert 2014-09-25 18:33:13

0

z = pop_three(x)意味着你逝去的列表x,然後變異使用列表xcollection.pop()x所以x正在因此發生突變不能== range(10)

y變得new_collectionzcollection這是突變返回值爲x

In [2]: x = range(10) 

In [3]: y, z = pop_three(x) 

In [4]: z is x # z is the object x 
Out[4]: True 
0

列表是可變的。從列表中彈出時,可以修改列表。

原來指向該列表將繼續指向相同的修改列表中的任何變量。您在列表中指向的任何新變量也將指向相同的修改列表。

1

因爲當你通過x到pop_tree(),你叫collection.pop()。這有效地做x.pop() 因此,當您從功能可按原來,X只得到了7元

相關問題