根据返回值修改收益率

时间:2016-04-12 19:43:49

标签: python python-3.x generator yield-from

我们说我有这些解析器:

parsers = {
    ".foo": parse_foo,
    ".bar", parse_bar
}

parse_fooparse_bar都是逐个生成行的生成器。如果我想创建一个调度函数,我会这样做:

def parse(ext):
    yield from parsers[ext]()

语法的收益率使我能够轻松地在生成器中上下传递信息。

在修改收益结果时是否有任何方法可以维持隧道效应? 在破坏隧道时这样做很容易:

def parse(ext):
    for result in parsers[ext]():
        # Add the extension to the result
        result.ext = ext
        yield result

但是这样我就无法将.send().throw()一直用于解析器。

我想到的唯一方法就是做一些像try: ... except Exception: ...这样丑陋的事情并传递异常,同时为.send()做同样的事情。它很丑陋,很杂乱,容易出错。

3 个答案:

答案 0 :(得分:2)

try ... yield ... except之外还有另一种方法:通过实现新的生成器。使用此类,您可以转换基础生成器的所有输入和输出:

identity = lambda x: x
class map_generator:
  def __init__(self, generator, outfn = identity,
      infn = identity, throwfn = identity):
    self.generator = generator
    self.outfn = outfn
    self.infn = infn
    self.throwfn = throwfn
    self.first = True
  def __iter__(self):
    return self
  def __next__(self):
    return self.send(None)
  def _transform(self, value):
    if self.first:
      self.first = False
      return value
    else:
      return self.infn(value)
  def send(self, value):
    return self.outfn(self.generator.send(self._transform(value)))
  def throw(self, value):
    return self.outfn(self.generator.throw(self.throwfn(value)))
  def next(self): # for python2 support
    return self.__next__()

用法:

def foo():
  for i in "123":
    print("sent to foo: ", (yield i))

def bar():
  dupe = lambda x:2*x
  tripe = lambda x:3*x
  yield from map_generator(foo(), dupe, tripe)

i = bar()
print("received from bar: ", i.send(None))
print("received from bar: ", i.send("B"))
print("received from bar: ", i.send("C"))

...

received from bar:  11
sent to foo:  BBB
received from bar:  22
sent to foo:  CCC
received from bar:  33

编辑:您可能希望从collections.Iterator继承,但在此用例中不是必需的。

答案 1 :(得分:0)

parse_fooparse_bar添加扩展程序:

def parse_foo(ext):
    # Existing code
    ...
    # Add an extension to the item(s)
    item.ext = ext

def parse(ext):
    yield from parsers[ext](ext)

或者只是在每个函数中对其进行硬编码:

def parse_foo():
    # Existing code
    ...
    # Add an extension to the item(s)
    item.ext = ".foo"

答案 2 :(得分:0)

不幸的是没有内置功能。您可以使用类自己实现它,但是名为cotoolz的包实现了一个map()函数,它正是这样做的。

他们的map函数比内置map()慢4倍,但是它对生成器协议有所了解,并且比类似的Python实现更快(它用C编写并且需要C99编译器)。

他们页面的一个例子:

>>> def my_coroutine():
...     yield (yield (yield 1))
>>> from cotoolz import comap
>>> cm = comap(lambda a: a + 1, my_coroutine())
>>> next(cm)
2
>>> cm.send(2)
3
>>> cm.send(3)
4
>>> cm.send(4)
Traceback (most recent call last):
    ...
StopIteration