[英]Why does Python's 'for … in' work differently on a list of values vs. a list of dictionaries?
我想知道如何在Python中工作的一些細節。
我的理解是每次迭代迭代的for var in iterable
都會創建一個變量var,綁定到iterable的當前值。 所以,如果你for c in cows; c = cows[whatever]
做for c in cows; c = cows[whatever]
for c in cows; c = cows[whatever]
,但在循環內改變c不會影響原始值。 但是,如果要為字典鍵分配值,它似乎會有所不同。
cows=[0,1,2,3,4,5]
for c in cows:
c+=2
#cows is now the same - [0,1,2,3,4,5]
cows=[{'cow':0},{'cow':1},{'cow':2},{'cow':3},{'cow':4},{'cow':5}]
for c in cows:
c['cow']+=2
# cows is now [{'cow': 2}, {'cow': 3}, {'cow': 4}, {'cow': 5}, {'cow': 6}, {'cow': 7}
#so, it's changed the original, unlike the previous example
我看到一個人可以使用枚舉來使第一個例子工作,但我想這是一個不同的故事。
cows=[0,1,2,3,4,5]
for i,c in enumerate(cows):
cows[i]+=1
# cows is now [1, 2, 3, 4, 5, 6]
為什么它會影響第二個示例中的原始列表值而不是第一個?
[編輯]
謝謝你的回答。 我從PHP的角度來看這個,你可以在foreach中使用&符號來指定你是在操作迭代的引用還是副本。 我現在看到真正的區別是python如何處理不可變對象的基本細節。
它有助於描繪每次迭代中c
所持有的引用會發生什么:
[ 0, 1, 2, 3, 4, 5 ]
^
|
c
c包含指向列表中第一個元素的引用。 當你做c += 2
(即c = c + 2
,臨時變量c
被重新分配一個新值。這個新值是2
, c
被反彈到這個新值。原始列表是單獨的。
[ 0, 1, 2, 3, 4, 5 ]
c -> 2
現在,在字典案例中,這是第一次迭代中c
綁定的內容:
[ {'cow':0}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ]
^
|
c
這里, c
指向字典對象{'cow':0}
。 當你做c['cow'] += 2
(即c['cow'] = c['cow'] + 2
)時,字典對象本身會發生變化,因為c
不會反彈到不相關的對象。 也就是說, c
仍然指向第一個字典對象。
[ {'cow':2}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ]
^
|
c
實際上並沒有采取不同的行動。 更改變量與更改變量的屬性不同。 您將在以下示例中看到相同的內容:
a = 1
b = a
b = 2
這里a仍然是1. b被賦予了不同的值,並且不再與a相同
a = {"hello": 1}
b = a
b["hello"] = 2
這里a["hello]
返回2,而不是1, b
仍是相同的值,因為我們沒有指派任何東西b
,因此b
是相同的a
。我們改變了財產["hello"]
的b
到2由於a
和b
是相同的變量a["hello"]
也是2
在兩種情況下, c
都是臨時的一次性變量。 (請記住,在Python中,所有變量都只是引用,綁定到它們所代表的對象,並且能夠反彈到不同的對象。在這方面,Python比某些其他語言更加一致。)
在列表示例中,每次迭代都會將c
從一個整數重新綁定到另一個整數,而原始列表保持不變。
在你的dict示例中,每次迭代都會訪問臨時綁定c
的dict,將其中一個dict的成員重新綁定到另一個整數。
在這兩種情況下,在循環結束時都會忽略c
,但由於在第二種情況下您更改了c
以外的數據結構,因此在循環完成時會注意到更改。
這與for ... in ...
無關。 將代碼從for c in cows:
更改for c in cows:
c = cows[3]
cows c = cows[3]
(並在下一行中顯示)並查看效果。
在第一個示例中,list元素是int對象; 他們是不變的。 在第二個例子中,它們是dict對象,它們是可變的。
在第一個循環中執行名稱分配僅重新綁定名稱。 在第二個循環中執行項目分配會修改現有對象。
在第二個例子,你有字典對象的列表。 c
引用在循環范圍內修改的字典對象。
無論循環如何,您都必須注意:
some_var = some_object
將名稱some_var
綁定到對象some_object
。 some_var
引用的上一個對象(如果有)是未綁定的。
some_var[some_index] = some_object
不綁定/取消綁定some_var
; 它只是以下的語法糖:
some_var.__setitem__(some_index, some_object)
顯然, some_var
仍然指向與之前相同的可索引(序列或映射)對象,剛剛被修改。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.