如何与生成器形成多个管道? [英] How do I form multiple pipelines with generators?

查看:69
本文介绍了如何与生成器形成多个管道?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using python and I'm trying to find a way to elegantly chain multiple generators together. An example of the problem would be having for instance a root generator which provides some kind of data and each value being passed to its "children" like a cascade which in turn may modify the object they receive. I could go for this route:

for x in gen1:
    gen2(x)
    gen3(x)

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

But it's ugly and not elegant. I was thinking about a more functional way of doing things.

推荐答案

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

You could turn the generators into coroutines so they can send() and receive values from one another (using (yield) expressions). This will give each one a chance to change the values they receive, and/or pass them along to the next generator/coroutine (or ignore them completely).

请注意,在下面的示例代码中,我使用一个名为coroutine的装饰器来启动"生成器/协程函数.这导致它们在第一个yield表达式/语句之前执行.这是此youtube视频中一个非常有启发性的演讲中显示的一个简短修改的版本,该演讲的标题为 A戴夫·比兹利(Dave Beazley)在PyCon 2009上提供的有关协程和并发的好奇课程 .

Note in the sample code below, I use a decorator named coroutine to "prime" the generator/coroutine functions. This causes them execute as far as just before their first yield expression/statement. It's a slightly modified version of one shown in this youtube video of a very illuminating talk titled A Curious Course on Coroutines and Concurrency that Dave Beazley gave at PyCon 2009.

从生成的输出中应该可以看到,数据值正在由通过单个send()配置到头部协程的每个管道处理,然后有效地将其多路复用"到每个管道下.由于每个子协程也都这样做,因此有可能建立一个精致的进程树".

As you should be able to see from the output produced, the data values are being processed by each pipeline that was configured via a single send() to the head coroutine, which then effectively "multiplexes" it down each pipeline. Since each sub-coroutine also does this, it would be possible to set up an elaborate "tree" of processes.

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)

输出:

piping 0
  gen1: 0 is divisible by 2
  gen2: 0 is divisible by 4
  gen3: 0 is divisible by 6
piping 1
piping 2
  gen1: 2 is divisible by 2
piping 3
piping 4
  gen1: 4 is divisible by 2
  gen2: 4 is divisible by 4
piping 5
piping 6
  gen1: 6 is divisible by 2
  gen3: 6 is divisible by 6
piping 7
piping 8
  gen1: 8 is divisible by 2
  gen2: 8 is divisible by 4
piping 9
piping 10
  gen1: 10 is divisible by 2
piping 11
piping 12
  gen1: 12 is divisible by 2
  gen2: 12 is divisible by 4
  gen3: 12 is divisible by 6
piping 13
piping 14
  gen1: 14 is divisible by 2
piping 15
piping 16
  gen1: 16 is divisible by 2
  gen2: 16 is divisible by 4

这篇关于如何与生成器形成多个管道?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆