簡體   English   中英

添加到同一字典的不同生成器返回相同的值

[英]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.

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