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

7 python generator-expression cartesian-product

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

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
Run Code Online (Sandbox Code Playgroud)

当生成器理解被列表推导替换时,工作正常.当只有2个迭代时也可以工作.但是,当我尝试

print(list(cart([1, 2, 3], 'ab', [4, 5])))
Run Code Online (Sandbox Code Playgroud)

我明白了

[(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)]
Run Code Online (Sandbox Code Playgroud)

为什么这个而不是笛卡儿产品?

Mar*_*ers 7

您正在创建生成器表达式,这些表达式for iterable in iterables[1:]:循环的下一次迭代之前不会迭代.他们正在使用闭包,这些闭包在运行时被查找.

在这方面,Generator表达式本质上是小函数,它们创建自己的作用域,并且父作用域中的任何名称都需要被视为闭包以使其工作.迭代时执行'function',只需要闭包并解析为引用变量的当前值.

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

(e1 + (e2,) for e1 in out for e2 in iterable)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

您可以使用返回生成器表达式的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
Run Code Online (Sandbox Code Playgroud)