Change exit from return value

Let's say I have these parsers:

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

parse_foo and parse_bar are both generators that produce strings one by one. If I want to create one submit function, I would do the following:

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

Exiting the syntax makes it easy to tunnel information up and down generators.

Is there a way to support tunneling while changing profitability results?
This happens when tunneling breaks:

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

But this way I cannot use .send() or .throw() all the way to the parser.

The only way I think of is to do something ugly like try: ... except Exception: ... and throw exceptions by doing the same for .send() . It is ugly, dirty and error prone.

+7
python generator yield-from
source share
3 answers

Unfortunately there is no built-in that does this . You can implement it yourself using classes, but a package called cotoolz implements the map() function, which does just that.

Their display function is 4 times slower than the built-in map() , but it knows the generator protocol and faster than a similar Python implementation (it is written in C and requires the C99 compiler).

Example from your page:

 >>> 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 
0
source share

There is another way to do this other than try ... yield ... except : by introducing a new generator. With this class, you can convert all the inputs and outputs of your base generator:

 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__() 

Using:

 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 

EDIT: you can inherit from collections.Iterator , but this is not necessary in this utility.

+2
source share

You have parse_foo and parse_bar add extensions:

 def parse_foo(ext): # Existing code ... # Add an extension to the item(s) item.ext = ext def parse(ext): yield from parsers[ext](ext) 

Or just copy it into each function:

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

All Articles