Python中生成器解包的怪异行为

时间:2018-10-02 07:38:53

标签: python python-3.x iterator generator iterable-unpacking

我目前正在尝试更加熟悉Python中的迭代器,并且遇到了一些奇怪的行为。本质上,生成器理解会导致错误的行为,而列表理解会导致正确的行为。

让我先解释一下我想做的事情,然后再说我得到的行为。
想象一下,有一个可迭代的字典,例如

d = {'a': [1, 2, 3], 'b': [4, 5]}

我想要的是列出所有迭代器可能组合的字典。对于第一个示例,这将是

l = [
        {'a': 1, 'b': 4},
        {'a': 1, 'b': 5},
        {'a': 2, 'b': 4},
        {'a': 2, 'b': 5},
        {'a': 3, 'b': 4},
        {'a': 3, 'b': 5},
    ]   

为此,我创建了这个生成器:

def dict_value_iterator(d):
    for k, v in d.items():         
        yield ((k, vi) for vi in v)

想法是运行以下代码以获得所需的结果

def get_all_dicts(d):
    return map(dict, *itertools.product(dict_value_iterator(d)))

现在,为了奇怪的行为。
为了测试dict_value_iterator生成器确实完成了我希望的功能,我运行了以下代码:

for i in dict_value_iterator(d):
    print(list(i))

这确实符合我的期望,即打印出以下内容:

[('a', 1), ('a', 2), ('a', 3)]
[('b', 4), ('b', 5)]

但是,当我运行以下代码时

def test_unpacking(*args):
    for a in args:
        print(list(a))
test_unpacking(*dict_value_iterator(d))

我得到了输出

[('b', 1), ('b', 2), ('b', 3)]
[('b', 4), ('b', 5)]

这对我来说几乎没有意义,为什么迭代器解包会改变任何东西。

最后的提示。
我发现它的方法是在d上运行get_all_dicts函数,结果如下:

[{'b': 4}, {'b': 5}, {'b': 4}, {'b': 5}, {'b': 4}, {'b': 5}]

但是,当我如下修改dict_value_iterator

def dict_value_iterator(d):
    for k, v in d.items():         
        yield ((k, vi) for vi in v)

我得到这个输出

[{'a': 1, 'b': 4},
 {'a': 1, 'b': 5},
 {'a': 2, 'b': 4},
 {'a': 2, 'b': 5},
 {'a': 3, 'b': 4},
 {'a': 3, 'b': 5}]

这就是我想要的。

1 个答案:

答案 0 :(得分:2)

这是简化版:

clipToBounds

仅存在一个名为generators = [] for i in [1, 2]: generators.append((i for _ in [1])) print(list(generators[0])) # [2] 的变量,并且i循环反复对其进行设置。生成器表达式创建的所有生成器都引用相同的for,直到退出循环后才读取它。

解决此问题的一种方法是通过使用函数创建另一个作用域(例如,像在ES5中那样):

i