簡體   English   中英

python生成器對象混淆

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

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