如何使用生成器形成多个管道?

War*_*ior 4 python generator

我正在使用 python,我正在尝试找到一种将多个生成器优雅地链接在一起的方法。问题的一个例子是,例如一个根生成器,它提供某种数据,每个值都像级联一样传递给它的“子级”,而级联又可以修改它们接收到的对象。我可以走这条路线:

for x in gen1:
    gen2(x)
    gen3(x)
Run Code Online (Sandbox Code Playgroud)

但它丑陋且不优雅。我正在考虑一种更实用的做事方式。

mar*_*eau 6

您可以将生成器转换为协程,以便它们可以相互send()接收值(使用(yield)表达式)。这将使每个人都有机会更改他们收到的值,和/或将它们传递给下一个生成器/协程(或完全忽略它们)。

请注意,在下面的示例代码中,我使用了一个名为coroutine“准备”生成器/协程函数的装饰器。这会导致它们在第一个yield表达式/语句之前执行。这是 youtube 视频中显示的一个稍微修改的版本,该视频是 Dave Beazley 在 2009 年 PyCon上发表的题为A Curious Course on Coroutines and Concurrency的非常有启发性的演讲。

正如您应该能够从生成的输出中看到的那样,数据值正在由每个管道处理,这些管道通过单个send()到 head 协程进行配置,然后有效地将其“多路复用”到每个管道中。由于每个子协程也这样做,因此可以建立一个精心设计的进程“树”。

import sys

def coroutine(func):
    """ Decorator to "prime" generators used as coroutines. """
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)  # Create coroutine generator function.
        next(cr)                   # Advance to just before its first yield.
        return cr
    return start

def pipe(name, value, divisor, coroutines):
    """ Utility function to send values to list of coroutines. """
    print('  {}: {} is divisible by {}'.format(name, value, divisor))
    for cr in coroutines:
        cr.send(value)

def this_func_name():
    """ Helper function that returns name of function calling it. """
    frame = sys._getframe(1)
    return frame.f_code.co_name


@coroutine
def gen1(*coroutines):
    while True:
        value = (yield)     # Receive values sent here via "send()".
        if value % 2 == 0:  # Only pipe even values.
            pipe(this_func_name(), value, 2, coroutines)

@coroutine
def gen2(*coroutines):
    while True:
        value = (yield)     # Receive values sent here via "send()".
        if value % 4 == 0:  # Only pipe values divisible by 4.
            pipe(this_func_name(), value, 4, coroutines)

@coroutine
def gen3(*coroutines):
    while True:
        value = (yield)     # Receive values sent here via "send()".
        if value % 6 == 0:  # Only pipe values divisible by 6.
            pipe(this_func_name(), value, 6, coroutines)

# Create and link together some coroutine pipelines.
g3 = gen3()
g2 = gen2()
g1 = gen1(g2, g3)

# Send values through both pipelines (g1 -> g2, and g1 -> g3) of coroutines.
for value in range(17):
    print('piping {}'.format(value))
    g1.send(value)
Run Code Online (Sandbox Code Playgroud)

输出:

import sys

def coroutine(func):
    """ Decorator to "prime" generators used as coroutines. """
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)  # Create coroutine generator function.
        next(cr)                   # Advance to just before its first yield.
        return cr
    return start

def pipe(name, value, divisor, coroutines):
    """ Utility function to send values to list of coroutines. """
    print('  {}: {} is divisible by {}'.format(name, value, divisor))
    for cr in coroutines:
        cr.send(value)

def this_func_name():
    """ Helper function that returns name of function calling it. """
    frame = sys._getframe(1)
    return frame.f_code.co_name


@coroutine
def gen1(*coroutines):
    while True:
        value = (yield)     # Receive values sent here via "send()".
        if value % 2 == 0:  # Only pipe even values.
            pipe(this_func_name(), value, 2, coroutines)

@coroutine
def gen2(*coroutines):
    while True:
        value = (yield)     # Receive values sent here via "send()".
        if value % 4 == 0:  # Only pipe values divisible by 4.
            pipe(this_func_name(), value, 4, coroutines)

@coroutine
def gen3(*coroutines):
    while True:
        value = (yield)     # Receive values sent here via "send()".
        if value % 6 == 0:  # Only pipe values divisible by 6.
            pipe(this_func_name(), value, 6, coroutines)

# Create and link together some coroutine pipelines.
g3 = gen3()
g2 = gen2()
g1 = gen1(g2, g3)

# Send values through both pipelines (g1 -> g2, and g1 -> g3) of coroutines.
for value in range(17):
    print('piping {}'.format(value))
    g1.send(value)
Run Code Online (Sandbox Code Playgroud)