[英]python generator object confusion
我對我的代碼有什么問題感到困惑:
users = [{'id': 1, 'name': 'Number1', 'age': 11},
{'id': 2, 'name': 'Number2', 'age': 12},
{'id': 3, 'name': 'Number3', 'age': 13},
{'id': 4, 'name': 'Number4', 'age': 14}]
_keys = ('name', 'age')
data_by_user_id = {u.get('id'): (u.get(k) for k in _keys) for u in users}
data_by_user_id看起來像:
{1: <generator object <genexpr> at 0x7f3c12c31050>, 2: <generator object <genexpr> at 0x7f3c12c310a0>, 3: <generator object <genexpr> at 0x7f3c12c310f0>, 4: <generator object <genexpr> at 0x7f3c12c31140>}
但迭代后:
for user_id, data in data_by_user_id.iteritems():
name, age = data
print user_id, name, age
結果與我預期的不同:
1 Number4 14
2 Number4 14
3 Number4 14
4 Number4 14
誰能解釋一下我在這做錯了什么? 我知道我可以使用列表理解而不是生成器,但我想知道我的代碼有什么問題
謝謝!
你在詞典理解陳述中的表達:
(u.get(k) for k in _keys)
是一個生成器表達式 。 這意味着你構建了一個生成器。 生成器是一個可迭代的對象,它懶惰地計算元素:它不從u
獲取元素,它推遲了這個操作,直到你為它調用next(..)
來獲取下一個元素。 所以你構建了這樣一本字典。
在for
循環體中,你寫道:
name, age = data
data
是項目的價值。 現在這意味着你要求Python“ 解包 ”迭代。 這將是有效的,因為可迭代的數量與元素的數量與左邊的變量數量完全相同,因此在這種情況下為2。 因此,您將耗盡發電機並獲得迭代器的結果。 接下來打印這些元素。
請注意,在for
循環之后,字典的所有值都將耗盡生成器,因此for
循環具有副作用。 為了防止這種情況,您可以更好地實現生成器。
編輯 :這里的另一個問題是,你在字典理解中使用u
,這不是很好的范圍。 因此,如果更改u
變量,則生成器的結果也將更改 。 這是有問題的,因為在字典理解結束時,所有生成器都將使用最后一個字典。
您可以通過生成本地范圍來解決問題:
{u.get('id'): (lambda u=u: (u.get(k) for k in _keys))() for u in users}
現在它生成預期的輸出:
>>> users = [{'id': 1, 'name': 'Number1', 'age': 11},
... {'id': 2, 'name': 'Number2', 'age': 12},
... {'id': 3, 'name': 'Number3', 'age': 13},
... {'id': 4, 'name': 'Number4', 'age': 14}]
>>>
>>> _keys = ('name', 'age')
>>> data_by_user_id = {u.get('id'): (lambda u=u: (u.get(k) for k in _keys))() for u in users}
>>> for user_id, data in data_by_user_id.iteritems():
... name, age = data
... print user_id, name, age
...
1 Number1 11
2 Number2 12
3 Number3 13
4 Number4 14
您可能已經知道,生成器表達式會被懶惰地評估。 dict.get
的評估推遲到生成器表達式被消耗,當前范圍中的u
是列表的最后一個字典:
>>> u = {'id': 1, 'name': 'Number1', 'age': 11}
>>> _keys = ('name', 'age')
>>> gen = (u.get(k) for k in _keys)
>>> # update u
>>> u = {'id': 4, 'name': 'Number4', 'age': 14}
>>> list(gen)
['Number4', 14]
解決此問題的一種顯而易見的方法是使用列表推導。 另一種方法,不如第一種方法,是將生成器表達式放在一個函數中,並通過默認參數將u
的當前值綁定到該函數:
data_by_user_id = {u.get('id'): lambda x=u: (x.get(k) for k in _keys) for u in users}
for user_id, data in data_by_user_id.iteritems():
name, age = data()
print name, age
Number1 11
Number2 12
Number3 13
Number4 14
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.