【问题标题】:How to 'flatten' generators in python?如何在 python 中“扁平化”生成器?
【发布时间】:2019-06-15 06:00:45
【问题描述】:

我在 python 中“扁平化”一些生成器时遇到问题。这是我的代码:

import itertools as it
test = [[1,2,3],[4,5],[6,7,8]]
def comb(possible):
    if len(possible) != 1:
        for a in possible[0]:
            yield from it.product((a,), comb(possible[1:]))
    else:
        yield from possible[0]
list(comb(test))

这给了我:

[(1, (4, 6)),
(1, (4, 7)),
(1, (4, 8)),
(1, (5, 6)),
(1, (5, 7)),
(1, (5, 8)),
(2, (4, 6)),
(2, (4, 7)),
(2, (4, 8)),
(2, (5, 6)),
(2, (5, 7)),
(2, (5, 8)),
(3, (4, 6)),
(3, (4, 7)),
(3, (4, 8)),
(3, (5, 6)),
(3, (5, 7)),
(3, (5, 8))]

但是,我想要类似的东西:

[(1, 4, 6),
(1, 4, 7),
(1, 4, 8),
(1, 5, 6),
(1, 5, 7),
(1, 5, 8),
(2, 4, 6),
(2, 4, 7),
(2, 4, 8),
(2, 5, 6),
(2, 5, 7),
(2, 5, 8),
(3, 4, 6),
(3, 4, 7),
(3, 4, 8),
(3, 5, 6),
(3, 5, 7),
(3, 5, 8)]

一般来说,该函数应该为我提供所有可能路径的生成器以通过列表,即from test[0] -> test[1] -> ... -> test[n],其中nlen(test)。在这里,它在每一步都选取一个元素。

类似于以下函数的返回,只是使用生成器:

def prod(possible):
    if len(possible) != 1:
        b = []
        for i in range(len(possible[0])):
            for x in prod(possible[1:]):
                if len(possible) == 2:
                    b += [[possible[0][i]]+[x]]
                else:
                    b += [[possible[0][i]]+x]
        return b
    else:
        return possible[0]
prod(test)

我玩过it.chainit.chain.from_iterable,但似乎无法正常工作。问题是我的“测试”列表的大小和长度是可变的,因此我必须递归地完成整个事情。

编辑:

itertools.product(*test)

正如约翰·科尔曼指出的那样工作

【问题讨论】:

  • 根据您的函数名称,您是否正在寻找itertools.combinations?无论哪种方式,仅从代码和输出示例都很难判断该函数应该做什么。
  • itertools.product(*test) ?
  • 你用的是什么版本的python?
  • 正如 John Coleman 所指出的,您似乎想要itertools.product。他的单线提供了一个生成器,可以产生你想要的。
  • itertools.product(*test) 完成了这项工作。我不知道我可以提出这样的论点。非常感谢。

标签: python recursion iterator itertools yield


【解决方案1】:

这是在不使用内置函数的情况下计算 product 列表的一种方法

def product (*iters):
  def loop (prod, first = [], *rest):
    if not rest:
      for x in first:
        yield prod + (x,)
    else:
      for x in first:
        yield from loop (prod + (x,), *rest)
  yield from loop ((), *iters)

for prod in product ("ab", "xyz"):
  print (prod)

# ('a', 'x')
# ('a', 'y')
# ('a', 'z')
# ('b', 'x')
# ('b', 'y')
# ('b', 'z')

在 python 中,我们可以使用list 构造函数将生成器的输出收集到一个列表中。请注意,我们还可以计算两个以上输入的乘积,如下所示

print (list (product ("+-", "ab", "xyz")))
# [ ('+', 'a', 'x')
# , ('+', 'a', 'y')
# , ('+', 'a', 'z')
# , ('+', 'b', 'x')
# , ('+', 'b', 'y')
# , ('+', 'b', 'z')
# , ('-', 'a', 'x')
# , ('-', 'a', 'y')
# , ('-', 'a', 'z')
# , ('-', 'b', 'x')
# , ('-', 'b', 'y')
# , ('-', 'b', 'z')
# ]

因为product 接受iterables 的列表,所以产品中可以使用任何可迭代的输入。它们甚至可以混合如下所示

print (list (product (['@', '%'], range (2), "xy")))
# [ ('@', 0, 'x')
# , ('@', 0, 'y')
# , ('@', 1, 'x')
# , ('@', 1, 'y')
# , ('%', 0, 'x')
# , ('%', 0, 'y')
# , ('%', 1, 'x')
# , ('%', 1, 'y')
# ]

因为product 被定义为生成器,所以即使在编写更复杂的程序时,我们也能获得很大的灵活性。考虑这个找到由整数组成的直角三角形的程序,Pythagorean triple。另请注意,product 允许您重复一个可迭代作为输入,如下面的product (r, r, r) 所示

def is_triple (prod):
  (a,b,c) = prod
  return a * a + b * b == c * c

def solver (n):
  r = range (1,n)
  for p in product (r, r, r):
    if is_triple (p):
      yield p

print (list (solution in solver (20)))
# (3, 4, 5)
# (4, 3, 5)
# (5, 12, 13)
# (6, 8, 10)
# (8, 6, 10)
# (8, 15, 17)
# (9, 12, 15)
# (12, 5, 13)
# (12, 9, 15)
# (15, 8, 17)

如需更多说明以及如何在不使用生成器的情况下执行此操作,请查看this answer

【讨论】:

    猜你喜欢
    • 2012-07-15
    • 2018-05-14
    • 2019-12-13
    • 1970-01-01
    • 1970-01-01
    • 2018-05-24
    • 1970-01-01
    • 1970-01-01
    • 2019-04-24
    相关资源
    最近更新 更多