如何在Python中加入两个生成器?

问题:如何在Python中加入两个生成器?

我想更改以下代码

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中加入两个生成器?

I want to change the following code

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

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

to this code:

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

I get the error:

unsupported operand type(s) for +: ‘generator’ and ‘generator’

How to join two generators in Python?


回答 0

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


回答 1

代码示例:

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

A example of code:

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

在Python(3.5或更高版本)中,您可以执行以下操作:

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

In Python (3.5 or greater) you can do:

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

回答 3

简单的例子:

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

Simple example:

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

回答 4

使用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)

With itertools.chain.from_iterable you can do things like:

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

在这里,它使用带有s 的生成器表达式for

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]

Here it is using a generator expression with nested fors:

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

也可以使用unpack运算符*

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

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

One can also use unpack operator *:

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

NOTE: Works most efficiently for ‘non-lazy’ iterables. Can also be used with different kind of comprehensions. Preferred way for generator concat would be from the answer from @Uduse


回答 7

如果要使生成器分开,但仍要同时遍历它们,则可以使用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

If you want to keep the generators separate but still iterate over them at the same time you can use zip():

NOTE: Iteration stops at the shorter of the two generators

For example:

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

回答 8

假设我们必须使用生成器(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

Lets say that we have to generators (gen1 and gen 2) and we want to perform some extra calculation that requires the outcome of both. We can return the outcome of such function/calculation through the map method, which in turn returns a generator that we can loop upon.

In this scenario, the function/calculation needs to be implemented via the lambda function. The tricky part is what we aim to do inside the map and its lambda function.

General form of proposed solution:

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

回答 9

所有那些复杂的解决方案…

做就是了:

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()

All those complicated solutions…

just do:

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

If you really want to “join” both generators, then do :

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