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

Weird behaviour of generator unpacking in Python

我目前正在尝试更熟悉 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)))

现在,对于 st运行ge 行为。
为了测试 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}]

这就是我想要的。

这是一个简化版本:

generators = []

for i in [1, 2]:
    generators.append((i for _ in [1]))

print(list(generators[0]))  # [2]

只有一个名为 i 的变量存在,for 循环重复设置它。生成器表达式创建的所有生成器都引用相同的 i 并且在循环退出之前不读取它。

修复它的一种方法是创建另一个带有函数的范围(例如,就像在 ES5 中那样):

def dict_value_iterator(d):
    def get_generator(k, v):
        return ((k, vi) for vi in v)

    for k, v in d.items():         
        yield get_generator(k, v)