繁体   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