修改 yield from 的 return 值
Modifying yield from's return value
假设我有这些解析器:
parsers = {
".foo": parse_foo,
".bar", parse_bar
}
parse_foo
和 parse_bar
都是逐行生成的生成器。如果我想创建一个单独的调度函数,我会这样做:
def parse(ext):
yield from parsers[ext]()
yield from 语法使我能够轻松地在生成器上下传送信息。
有没有办法在修改产量结果的同时保持隧道效应?
在打破隧道的同时这样做很容易:
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()
做同样的事情。它丑陋、凌乱且容易出错。
让 parse_foo
和 parse_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"
除了try ... yield ... except
之外,还有另一种方法可以做到这一点:实现一个新的生成器。使用此 class 您可以转换底层生成器的所有输入和输出:
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
继承,但在这个用例中它不是必需的。
不幸的是没有内置的。您可以使用 类 自己实现它,但是一个名为 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
假设我有这些解析器:
parsers = {
".foo": parse_foo,
".bar", parse_bar
}
parse_foo
和 parse_bar
都是逐行生成的生成器。如果我想创建一个单独的调度函数,我会这样做:
def parse(ext):
yield from parsers[ext]()
yield from 语法使我能够轻松地在生成器上下传送信息。
有没有办法在修改产量结果的同时保持隧道效应?
在打破隧道的同时这样做很容易:
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()
做同样的事情。它丑陋、凌乱且容易出错。
让 parse_foo
和 parse_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"
除了try ... yield ... except
之外,还有另一种方法可以做到这一点:实现一个新的生成器。使用此 class 您可以转换底层生成器的所有输入和输出:
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
继承,但在这个用例中它不是必需的。
不幸的是没有内置的。您可以使用 类 自己实现它,但是一个名为 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