簡體   English   中英

為什么Python的'for ... in'在值列表和字典列表上的工作方式不同?

[英]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被重新分配一個新值。這個新值是2c被反彈到這個新值。原始列表是單獨的。

[ 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由於ab是相同的變量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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM