[英]Different generators added to the same dictionary return the same values
我剛剛遇到一個奇怪的Python(3.7.0)行為,我真的不明白,看起來像是一個bug。 我想用生成器創建一個字典,但不知怎的,它們都返回相同的值。 這是我正在談論的代碼示例:
import itertools
d = {
"a": [-1, 2],
"b": [1, 2],
"c": [20, 20]
}
g = dict()
g2 = dict()
for letter, values in d.items():
g[letter] = (values[0] * values[1] * x for x in itertools.count())
g2[letter] = [values[0] * values[1] * x for x in range(3)]
for i in range(3):
for l, v in g.items():
print(v.__next__())
print(g2)
從我的觀點來看,g2元素和g生成器的預期輸出是相同的,但是我總是從最新的生成器接收值:
0
0
0
400
400
400
800
800
800
{'a': [0, -2, -4], 'b': [0, 2, 4], 'c': [0, 400, 800]}
總而言之,我做錯了嗎? 或者它只是標准的Python行為?
此錯誤不是由於生成器造成的。 這是一個范圍錯誤。
在生成器聲明中,使用名稱values
,盡管直到循環完成后才會執行生成器,其中values
現在是列表中的最后一項。 這是一個重現錯誤的示例。
for i in [1]:
g = (i for _ in range(3))
i = 'some new value'
print(next(g)) # 'some new value'
換句話說, values[0]
和values[1]
未綁定到生成器,如果名稱values
后面的values
發生更改,則生成器輸出也會發生變化。
這意味着您希望生成器周圍的閉包存儲值values[0]
和values[1]
。 您可以通過將生成器定義為函數來實現。
import itertools
# Here is a function taht will return a generator
def gen(a, b):
for x in itertools.count():
yield a * b * x
d = {"a": [-1, 2], "b": [1, 2], "c": [20, 20]}
g, g2 = dict(), dict()
for letter, values in d.items():
g[letter] = gen(values[0], values[1])
g2[letter] = [values[0] * values[1] * x for x in range(3)]
for i in range(3):
for l, v in g.items():
print(next(v))
print(g2)
實際上,由於這個原因,很少使用內聯生成器。 支持創建生成器的def-yield
方式。
另外,不要調用__next__
,而是使用內置的next
。
0
0
0
-2
2
400
-4
4
800
{'a': [0, -2, -4], 'b': [0, 2, 4], 'c': [0, 400, 800]}
查看代碼的固定版本:
import itertools
def make_generator(values):
return (values[0] * values[1] * x for x in itertools.count())
d = {
"a": [-1, 2],
"b": [1, 2],
"c": [20, 20]
}
g = dict()
g2 = dict()
for letter, values in d.items():
g[letter] = make_generator(values)
g2[letter] = [values[0] * values[1] * x for x in range(3)]
for i in range(3):
for l, v in g.items():
print(v.__next__())
print(g2)
打印出來:
0
0
0
-2
2
400
-4
4
800
{'a': [0, -2, -4], 'b': [0, 2, 4], 'c': [0, 400, 800]}
關鍵是在您的代碼中,所有生成器在本地范圍內使用相同的values
變量,因此它們最終都使用了dict中最后一個鍵的values
。 在我的版本中,每個生成器使用正確的values
因為每個生成器都在單獨的范圍內創建。
對於g2
的列表g2
不會發生這種情況,因為它們會在本地范圍內使用正確的values
立即求values
,並且稍后在values
已被覆蓋時對生成器進行求values
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.