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