如何将Python的itertools.product从列表推导转换为普通的for循环?
根据这个链接,下面这个函数的功能和他们的库是一样的(我把一些不需要的部分去掉了):
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)
在我的例子中,我传递给这个产品函数的是3个列表,但我需要添加一些条件检查,这样就不会把某些列表中的项目和其他列表中的项目混在一起,除非它们符合要求。所以我想我需要把:
result = [x+[y] for x in result for y in pool]
转换成“普通的”FOR循环(我不太确定该怎么称呼它们),这样我就可以添加几个IF检查,来确认列表中的项目是否应该混合在一起。
让我感到困惑的是,“x”是在遍历一个空的“result”列表,但在遍历的过程中又往里面添加了项目,所以我觉得这让转换成普通循环变得复杂了。
这是我尝试过的其中一个例子:
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)
任何帮助都非常感谢!
3 个回答
注意,在这一行 result = [x+[y] for x in result for y in pool]
中,result
出现了两次,但这并不重要。这个表达式是用旧的 result
来构建一个新的列表,然后把这个新列表赋值给 result
。
这可能让你感到困惑。一个等价的详细版本可以是:
def product(*args):
pools = map(tuple, args)
result = [[]]
for pool in pools:
tmp = []
for x in result: # note that it's the old 'result' here
for y in pool:
tmp.append(x+[y])
result = tmp
for prod in result:
yield tuple(prod)
这个产品函数其实是在把多个列表进行相乘,类似于一种归约操作。如果你想在这个过程中筛选结果,这样的做法可能对你没什么帮助。相反,你应该写一个产品函数,它可以接收固定数量的列表:
for x in list1:
for y in list2:
for z in list3:
if condition(x, y, z):
yield tuple(x, y, z)
你已经很接近了:嵌套列表推导式的右边部分是按照你写循环的顺序来写的,所以这一点你做得对。不过,在列表推导式的版本中,首先会计算赋值的右边部分,然后再把结果绑定到左边的变量上。所以
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))
更有好处,取决于你能剪掉多少分支,因为正如你所看到的,它会在内存中构建所有的中间列表。