Skip to main content
 首页 » 编程设计

python之如何将 Python 的 itertools.product 库从列表理解转换为普通的 for 循环

2025年05月04日9qlqwjy

根据 http://docs.python.org/2/library/itertools.html#itertools.product以下函数等同于使用他们的库(我从中删除了一些我不需要的东西):

def product(*args): 
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy 
    pools = map(tuple, args) 
    result = [[]] 
    for pool in pools: 
        result = [x+[y] for x in result for y in pool] 
    for prod in result: 
        yield tuple(prod) 

在我的例子中,我传递了 product function 3 列表,但我需要添加一些条件检查,因此它不会将一个列表中的某些项目与另一个列表中的项目混合,如果它们不符合要求。所以我认为我需要做的是转换:

result = [x+[y] for x in result for y in pool] 

进入“正常”FOR 循环(不确定如何引用它们),因此我可以添加几个 IF 检查来验证列表中的项目是否应该混合在一起。

主要让我感到困惑的是“x”正在遍历空的“结果”列表,但是在它迭代时会向其中添加项目,所以我认为这就是使我转换为正常循环变得复杂的原因。

这是我的一个尝试:

def product(*args): 
    pools = map(tuple, args) 
    result = [[]] 
    for pool in pools: 
        for x in result: 
            for y in pool: 
                result.append(x+[y]) 
    for prod in result: 
        yield tuple(prod) 

非常感谢任何帮助!

请您参考如下方法:

您非常接近:嵌套列表推导式右侧的书写顺序与您编写 for 循环的顺序相同,所以您做对了。但是,在 listcomp 版本中,首先计算赋值的 RHS,然后将其绑定(bind)到 LHS 上的名称。所以

result = [x+[y] for x in result for y in pool] 

需要成为

new_result = [] 
for x in result: 
    for y in pool: 
        new_result.append(x+[y]) 
result = new_result 

这样您就不会在迭代时修改 result。如果您想禁止某些安排——并且您可以按照适用于从左到右填充的迭代顺序的方式编写约束——那么您可以这样做:

def filtered_product(args, filter_fn): 
    pools = map(tuple, args) 
    result = [[]] 
    for pool in pools: 
        new_result = [] 
        for x in result: 
            for y in pool: 
                new_val = x+[y] 
                if filter_fn(new_val): 
                    new_result.append(x+[y]) 
        result = new_result 
        print 'intermediate result:', result 
    for prod in result: 
        yield tuple(prod) 

给出

In [25]: list(filtered_product([[1,2,3], [4,5,6], [7,8,9]], lambda x: sum(x) % 3 != 2)) 
intermediate result: [[1], [3]] 
intermediate result: [[1, 5], [1, 6], [3, 4], [3, 6]] 
intermediate result: [[1, 5, 7], [1, 5, 9], [1, 6, 8], [1, 6, 9], [3, 4, 8], [3, 4, 9], [3, 6, 7], [3, 6, 9]] 
Out[25]:  
[(1, 5, 7), 
 (1, 5, 9), 
 (1, 6, 8), 
 (1, 6, 9), 
 (3, 4, 8), 
 (3, 4, 9), 
 (3, 6, 7), 
 (3, 6, 9)] 

与简单地使用 (p for p in itertools.product(whatever) if condition(p)) 相比,这是否会给您带来任何好处将取决于您可以修剪多少分支,因为作为你可以看到它在内存中构造了所有中间列表。