如何嵌套 itertools 产品?

Posted

技术标签:

【中文标题】如何嵌套 itertools 产品?【英文标题】:How to nest itertools products? 【发布时间】:2018-01-23 06:57:55 【问题描述】:

给定一个列表,我可以得到列表中每个项目的乘积:

from itertools import product
x = 'apple orange pair None'.split()
[i + ' ' + j for i, j in product(x, x)]

[出]:

['apple apple',
 'apple orange',
 'apple pair',
 'apple None',
 'orange apple',
 'orange orange',
 'orange pair',
 'orange None',
 'pair apple',
 'pair orange',
 'pair pair',
 'pair None',
 'None apple',
 'None orange',
 'None pair',
 'None None']

如果我想将product(list, list) 的输出与初始列表嵌套,我可以这样做:

from itertools import product
x = 'apple orange pair None'.split()
for i, j in product(x, x):
    i = '' if i == 'None' else i
    j = '' if j == 'None' else j
    y = i + ' ' + j
    y = y.strip()
    print(y)
    for k, l in product(x, [y]):
        k = '' if k == 'None' else k
        l = '' if l == 'None' else l
        z = k + ' ' + l
        z = z.strip()
        print(z) 

[出]:

apple apple
apple apple apple
orange apple apple
pair apple apple
apple apple
apple orange
apple apple orange
orange apple orange
pair apple orange
apple orange
apple pair
apple apple pair
orange apple pair
pair apple pair
apple pair
apple
apple apple
orange apple
pair apple
apple
orange apple
apple orange apple
orange orange apple
pair orange apple
orange apple
orange orange
apple orange orange
orange orange orange
pair orange orange
orange orange
orange pair
apple orange pair
orange orange pair
pair orange pair
orange pair
orange
apple orange
orange orange
pair orange
orange
pair apple
apple pair apple
orange pair apple
pair pair apple
pair apple
pair orange
apple pair orange
orange pair orange
pair pair orange
pair orange
pair pair
apple pair pair
orange pair pair
pair pair pair
pair pair
pair
apple pair
orange pair
pair pair
pair
apple
apple apple
orange apple
pair apple
apple
orange
apple orange
orange orange
pair orange
orange
pair
apple pair
orange pair
pair pair
pair

apple
orange
pair

如果我想达到另一个层次的嵌套,我可以硬编码:

from itertools import product
x = 'apple orange pair None'.split()
for i, j in product(x, x):
    i = '' if i == 'None' else i
    j = '' if j == 'None' else j
    y = i + ' ' + j
    y = y.strip()
    print(y)
    for k, l in product(x, [y]):
        k = '' if k == 'None' else k
        l = '' if l == 'None' else l
        z = k + ' ' + l
        z = z.strip()
        print(z)
        for m, n in product(x, [z]):
            m = '' if m == 'None' else m
            n = '' if n == 'None' else n
            zz = m + ' ' + n
            zz = zz.strip()
            print(zz)

但是有没有其他方法可以在不进行硬编码的情况下实现相同的输出?

【问题讨论】:

我真的不明白你在后面的例子中得到了什么。例如,您的第二个示例多次包含“apple apple”。那是你要的吗?为什么将“无”放在原始列表中,然后又将其取出?你为什么要加入带有空格的项目,然后再取出空格? “将 product(list, list) 的输出与初始列表嵌套”是什么意思?对k 的各种值执行itertools.product(original_list, repeat=k) 无法实现您想要什么? 啊,repeat 正是我要找的东西!! 更准确地说,我试图得到product(x, repeat=1) + product(x, repeat=2) + product(x, repeat=3) =) docs.python.org/3/library/…? 【参考方案1】:

关键在于递归过程自然形成递归模式。

为了说明这个想法,'None' 不会被 '' 替换,因为它很简单。在进一步的解决方案中,它是针对嵌套模式完成的。

def product_combine(a, b):
  return [i + ' ' + j for i, j in product(a, b)]

# for n times of nesting
def products_combine(x, n):
  if n == 0:
    return x
  else:
    return product_combine(x, products_combine(x, n-1)) + products_combine(x, n-1)

x = 'apple orange pair None'.split()    
print(products_combine(x, 3))

如果以防万一,您需要不同的数据类型来保存结果。更通用的解决方案允许更灵活地选择输出数据类型:

# for different types of combination
def products_combine(combine):
  def products(x, n):
    if n == 0:
      return x
    else:
      return combine(x, products(x, n-1)) + products(x, n-1)
  return products

# combine to string ('None' is replaced for nested patterns, not for initial)
def tostr(a, b):
  NoneToEmpty = lambda x: '' if x == 'None' else x
  return [' '.join(map(NoneToEmpty, (i, j))).strip() for i, j in product(a, b)]

# combine to iterator (list/tuple)
def toiter(iter_type):
  def to_thatiter(a, b):
    return [iter_type((i,))+j if isinstance(j, iter_type) else iter_type((i, j)) for i, j in product(a, b)]
  return to_thatiter

tolist=toiter(list)
totuple=toiter(tuple)

products_str=products_combine(tostr)
products_list=products_combine(tolist)
products_tuple=products_combine(totuple)

x = 'apple orange pair None'.split()
print(products_str(x, 3))
print(products_list(x, 3))
print(products_tuple(x, 3))

一般形式是任何binary functionf(x, x),将其嵌套到f(x, f(x, f(x,...f(x, f(x, x))。不仅适用于产品而且适用于任意二元运算的通用方法是:

def nest(f_binary, n):
  def g(x):
    if n == 1:
      return x
    else:
      return f_binary(x, nest(f_binary, n-1)(x))
  return g

add = lambda x, y: x + y
power = lambda x,y: x**y
concatenate = lambda l1, l2: l1 + l2

x = 'apple orange pair None'.split()
print(list(nest(product, 3)(x)))
print(nest(add, 3)(5))
print(nest(power,3)(5))
print(nest(concatenate, 3)(['a','b']))

另一种想法是使用参数数量而不是显式整数 N 来指示嵌套级别。它看起来很奇怪,但它确实有效。

def nest(f):
  def expand(x, *args):
    return x if not args else f(x, expand(*args))
  return expand

products = nest(product)

x = 'apple orange pair None'.split()

# instead of giving N, you call products with number n of argument x
# indicating n levels of nesting (here: 3 x, product(x, product(x, x))
print(list(products(x, x, x)))

【讨论】:

以上是关于如何嵌套 itertools 产品?的主要内容,如果未能解决你的问题,请参考以下文章

itertools.product 比嵌套 for 循环慢

一日一技:如何让 itertools.tee 线程安全

使用 Itertools 具有内部中断的可变级别的嵌套循环

python 在itertools中使用product方法来避免嵌套for循环

Itertools 等效于嵌套循环“for x in xs: for y in ys...”

使用嵌套迭代器有啥意义吗?