为什么我的笛卡尔积函数不起作用?

Why doesnt my cartesian product function work?

考虑以下函数,其输出应该是可迭代序列的笛卡尔积:

def cart(*iterables):
    out = ((e,) for e in iterables[0])
    for iterable in iterables[1:]:
        out = (e1 + (e2,) for e1 in out for e2 in iterable)
    return out

用列表推导替换生成器推导时工作正常。当只有 2 个可迭代对象时也有效。但是当我尝试

print(list(cart([1, 2, 3], 'ab', [4, 5])))

我明白了

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

为什么是这个而不是笛卡尔积?

您正在创建 生成器表达式 ,直到 for iterable in iterables[1:]: 循环的下一次迭代才对其进行迭代。他们正在使用 闭包,在运行时查找。

生成器表达式在这方面本质上是小函数,它们创建自己的作用域,父作用域中的任何名称都需要被视为闭包才能工作。 'function' 在迭代时执行,然后才需要闭包并将其解析为引用变量的 current 值。

所以你创建一个这样的生成器表达式:

(e1 + (e2,) for e1 in out for e2 in iterable)

其中 iterable 是从父作用域(您的函数局部变量)获取的闭包。但是查找直到循环时的下一次迭代才完成,此时 iterable 是序列中的下一个元素.

因此,对于 [1, 2, 3], 'ab', [4, 5] 的输入,您在 iterable = 'ab' 时创建了一个生成器表达式,但是当您实际迭代时,for 循环已经分配了一个新值,现在是iterable = [4, 5]。当您最终遍历最终(链式)生成器时,只有对 iterable 的最后一次赋值才算数。

您在 iterables[0], iterables[-1] * len(iterables) - 1 上有效地创建了一个产品; iterables[1]iterables[-2] 被完全跳过,全部替换为 iterables[-1]

您可以使用生成器 函数 来避免关闭问题,传入 iterable 以绑定到本地:

def gen_step(out, iterable):
    for e1 in out:
        for e2 in iterable:
            yield e1 + (e2,)

def cart(*iterables):
    out = ((e,) for e in iterables[0])
    for iterable in iterables[1:]:
        out = gen_step(out, iterable)
    return out

您可以对返回生成器表达式的 lambda 执行相同的操作:

def cart(*iterables):
    out = ((e,) for e in iterables[0])
    for iterable in iterables[1:]:
        out = (lambda it=iterable: (e1 + (e2,) for e1 in out for e2 in it))()
    return out