要回答您的问题,我们需要查看itertools.product 的实现:
def product(*args, repeat=1):
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
here你找到了真正的C实现,但是要回答这个问题,参考python就足够了(见底部的EXTRA段)。
关注这行代码:
pools = [tuple(pool) for pool in args] * repeat
这样,两个迭代器的所有元素(取入输入)都转化为一个元组列表(仅在您第一次调用next()时),此时它们才真正被创建。
回到您的代码,当您第一次调用next(l) 时,会创建迭代器的所有元素。在您的示例中,将使用以下元素创建 polls 列表:
# pools: [(0, 1, 2), (0, 1, 2)]
这就是你得到这些输出的原因。
至于print("Here"),要了解为什么要先打印它,您需要了解生成器的工作原理:
itertool.product() 返回一个生成器对象。生成器在受到第一个next() 刺激之前不会执行功能代码。随后,每次调用next() 都允许您计算下一个元素,只执行一次包含关键字yield 的循环。
Here 你会找到很好的资源来更好地理解 python 生成器是如何工作的。
为什么“itertools”选择将元组列表保存在内存中?
因为笛卡尔积必须多次计算同一个元素,而迭代器不能只使用一次。
额外
在 C 中,元组 pools 的列表与 python 等效,正如您从这段代码中看到的那样,它被急切地评估。每个可迭代参数首先转换为一个元组:
pools = PyTuple_New(npools);
if (pools == NULL)
goto error;
for (i=0; i < nargs ; ++i) {
PyObject *item = PyTuple_GET_ITEM(args, i);
PyObject *pool = PySequence_Tuple(item);
if (pool == NULL)
goto error;
PyTuple_SET_ITEM(pools, i, pool);
indices[i] = 0;
}
for ( ; i < npools; ++i) {
PyObject *pool = PyTuple_GET_ITEM(pools, i - nargs);
Py_INCREF(pool);
PyTuple_SET_ITEM(pools, i, pool);
indices[i] = 0;
}