2013-12-19 111 views
2

這是一個有效的Python行爲?我認爲最終結果應該是[0,0,0],並且id()函數應該在每次迭代中返回相同的值。如何使pythonic,而不是使用枚舉或範圍(len(bar))?python將值分配給循環中的列表元素

bar = [1,2,3] 
print bar 
for foo in bar: 
    print id (foo) 
    foo=0 
    print id(foo) 
print bar 

輸出:

[1, 2, 3] 
5169664 
5169676 
5169652 
5169676 
5169640 
5169676 
[1, 2, 3] 

回答

19

第一所有的,你不能重新分配一個循環變量 - 你可以,但是這不會改變你正在迭代的列表。因此,設置foo = 0不會更改列表,而只會改變局部變量foo(它恰好包含每次迭代開始時的迭代值)。

接下來的事情,小的數字,像01在內部保持在小整數對象池(這是CPython的執行細節,並不一定是這樣!)這就是爲什麼ID是相同的foo之後您將0分配給它。該ID基本上是池中該整數對象0的ID。

如果您想在迭代時更改列表,您將不得不通過索引訪問元素。所以,如果你想保持輸出相同,但最後有[0, 0, 0],你將不得不遍歷索引:

for i in range(len(bar)): 
    print id(bar[i]) 
    bar[i] = 0 
    print id(bar[i]) 
print bar 

否則,只要你存儲列表的元素是不是真的有可能,因爲在一個變量中,你有一個單獨的引用,它與存儲在列表中的鏈接沒有鏈接。由於這些對象中的大多數都是不可變的,並且在爲變量分配新值時創建了一個新對象,因此您不會獲得列表的引用來更新。

+4

+1討論CPython的實現細節。這意味着,如果您可以更改存儲在「5169664」處的值,那麼這樣做會是災難性的,因爲它會改變當前設置爲1的_every_變量。 – Kevin

+3

「枚舉」可能比使用len(bar) '。 – Matthias

+0

我認爲這是問題部分的正確答案:爲什麼會發生?什麼是pythonic的方式來做到這一點?請參閱@Kevin答案 –

2

是的,你得到的輸出是普通的Python行爲。爲foo分配一個新值將更改foo的ID,並且不會更改bar中存儲的值。

如果你只是想零的列表,你可以這樣做:

bar = [0] * len(bar) 

如果你想要做一些更復雜的邏輯,在新的分配依賴於舊值,你可以用一個列表理解:

bar = [x * 2 for x in bar] 

或者你可以使用map

def double(x): 
    return x * 2 

bar = map(double, bar) 
+0

我認爲這是問題部分的正確答案:什麼是pythonic的方式來做到這一點?爲什麼會發生?請參閱@Poke answer –

0

你其實根本沒有改變列表。 循環的第一件事就是將bar [0]賦值給foo(相當於foo = bar [0])。 foo只是對1的引用。然後,將另一個onject 0賦值給foo。這將foo的引用更改爲0.但是您並沒有更改bar [0]。請記住,foo作爲變量引用bar [0],但將另一個值/對象分配給foo根本不會影響bar [0]。

0
bar = [0 for x in bar] 

長答案:foo只是一個本地名稱,重新綁定不會影響列表。 Python變量實際上只是關鍵:值對,而不是內存位置的符號名稱。

+0

由於您的for將會返回一個列表,你在技術上不需要[:] – scohe001

+0

@Josh yeps對。編輯該片段。 –

相關問題