如何在Python中连接两个生成器?

时间:2010-07-09 08:29:06

标签: python generator

我想更改以下代码

for directory, dirs, files in os.walk(directory_1):
    do_something()

for directory, dirs, files in os.walk(directory_2):
    do_something()

到此代码:

for directory, dirs, files in os.walk(directory_1) + os.walk(directory_2):
    do_something()

我收到错误:

  

+:'generator'和'generator'

的不支持的操作数类型

如何在Python中连接两个生成器?

13 个答案:

答案 0 :(得分:189)

我认为itertools.chain()应该这样做。

答案 1 :(得分:50)

代码示例:

from itertools import chain

def generator1():
    for item in 'abcdef':
        yield item

def generator2():
    for item in '123456':
        yield item

generator3 = chain(generator1(), generator2())
for item in generator3:
    print item

答案 2 :(得分:22)

简单示例:

from itertools import chain
x = iter([1,2,3])      #Create Generator Object (listiterator)
y = iter([3,4,5])      #another one
result = chain(x, y)   #Chained x and y

答案 3 :(得分:19)

在Python(3.5或更高版本)中,你可以这样做:

def concat(a, b):
    yield from a
    yield from b

答案 4 :(得分:8)

使用itertools.chain.from_iterable,您可以执行以下操作:

def genny(start):
  for x in range(start, start+3):
    yield x

y = [1, 2]
ab = [o for o in itertools.chain.from_iterable(genny(x) for x in y)]
print(ab)

答案 5 :(得分:6)

此处使用generator expression嵌套for s:

a = range(3)
b = range(5)
ab = (i for it in (a, b) for i in it)
assert list(ab) == [0, 1, 2, 0, 1, 2, 3, 4]

答案 6 :(得分:1)

如果你想让发生器分开,但仍然可以同时迭代它们,你可以使用zip():

注意:迭代在两个发生器中较短的一个停止

例如:

for (root1, dir1, files1), (root2, dir2, files2) in zip(os.walk(path1), os.walk(path2)):

    for file in files1:
        #do something with first list of files

    for file in files2:
        #do something with second list of files

答案 7 :(得分:1)

让我们说我们必须使用生成器(gen1和gen 2),我们想要执行一些额外的计算,这需要两者的结果。 我们可以通过map方法返回这样的函数/计算的结果,map方法又返回一个我们可以循环的生成器。

在这种情况下,需要通过lambda函数实现函数/计算。 棘手的部分是我们在地图内部及其lambda函数的目标。

建议解决方案的一般形式:

def function(gen1,gen2):
        for item in map(lambda x, y: do_somethin(x,y), gen1, gen2):
            yield item

答案 8 :(得分:1)

一个人也可以使用解包运算符*

concat = (*gen1(), *gen2())

注意:对于“非惰性”可迭代对象,其工作效率最高。也可以与其他种类的理解一起使用。生成器concat的首选方法是从@Uduse

的答案

答案 9 :(得分:1)

2020更新:在python 3和python 2中均可使用

import itertools

iterA = range(10,15)
iterB = range(15,20)
iterC = range(20,25)
### first option

for i in itertools.chain(iterA, iterB, iterC):
    print(i)

# 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
### alternative option, introduced in python 2.6

for i in itertools.chain.from_iterable( [iterA, iterB, iterC] ):
    print(i)

# 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

itertools.chain()是基础。

如果您有可迭代项,则

itertools.chain.from_iterables很方便。例如,每个子目录的文件列表,例如[ ["src/server.py", "src/readme.txt"], ["test/test.py"] ]

答案 10 :(得分:0)

所有这些复杂的解决方案...

只需:

for dir in director_1, directory_2:
    for directory, dirs, files in os.walk(dir):
        do_something()

如果您真的想“加入”两个生成器,请执行以下操作:

for directory, dirs, files in 
        [x for osw in [os.walk(director_1), os.walk(director_2)] 
               for x in osw]:
    do_something()

答案 11 :(得分:0)

我想说,正如用户“ wjandrea”的评论所建议的那样,最好的解决方案是

def concat_generators(*args):
    for gen in args:
        yield from gen

它不会更改返回的类型,并且确实是pythonic。

答案 12 :(得分:0)

(免责声明:仅限 Python 3!)

与您想要的语法类似的东西是使用 splat 运算符来扩展两个生成器:

for directory, dirs, files in (*os.walk(directory_1), *os.walk(directory_2)):
    do_something()

说明:

这有效地将两个生成器单级展平为一个 N 元组的 3 元组(来自 os.walk),如下所示:

((directory1, dirs1, files1), (directory2, dirs2, files2), ...)

你的 for 循环然后迭代这个 N 元组。

当然,通过简单地用方括号替换外圆括号,您可以获得三元组的列表而不是三元组的 N 元组:

for directory, dirs, files in [*os.walk(directory_1), *os.walk(directory_2)]:
    do_something()

这会产生类似的结果:

[(directory1, dirs1, files1), (directory2, dirs2, files2), ...]

专业:

这种方法的好处是您不必导入任何东西,而且代码也不多。

缺点:

缺点是您将两个生成器转储到一个集合中,然后迭代该集合,有效地执行两次传递并可能使用大量内存。