Изменение доходности от возвращаемого значения
Допустим, у меня есть эти парсеры:
parsers = {
".foo": parse_foo,
".bar", parse_bar
}
parse_foo
а также parse_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 ответа
К сожалению, нет встроенного, который делает это. Вы можете реализовать это самостоятельно, используя классы, но пакет cotoolz реализует map()
функция, которая делает именно это.
Их функция отображения в 4 раза медленнее встроенной map()
но он осведомлен о протоколе генератора и быстрее, чем аналогичная реализация 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
Есть другой способ сделать это, кроме 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
, но это не обязательно в этом случае использования.
Есть 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"