2016-04-12 9 views
7

Lassen Sie uns sagen, ich habe diese Parser:Ändern Ausbeute von Rückgabewert

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

parse_foo und parse_bar sind beide Generatoren, die Zeilen eins nach dem anderen ergeben. Wenn ich eine einzelne Dispatch-Funktion erstellen möchten, würde ich dies tun:

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

Die Ausbeute von Syntax mir Tunnel-Informationen ermöglicht leicht nach oben und unten den Generatoren.

Gibt es eine Möglichkeit, das Tunneln beizubehalten, während die Ertragsergebnisse modifiziert werden?
tun so, während der Tunnel brechen ist einfach:

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

Aber diese Art und Weise kann ich .send() oder .throw() den ganzen Weg an den Parser nicht verwenden.

Der einzige Weg, an den ich denke, ist, indem Sie etwas Hässliches wie try: ... except Exception: ... tun und die Ausnahmen bestehen, während Sie das gleiche für .send() tun. Es ist hässlich, chaotisch und fehleranfällig.

+0

Ich denke, Ihre beste Wette wahrscheinlich ein 'passthrough_map' zu implementieren wäre das tut, was' map' tut, während vorbei 'send' und' throw' bis zum Generator du mappst vorbei. IIRC, dieses Recht zu tun ist schwierig, aber Sie müssen es nur einmal richtig machen, und dann können Sie es wiederverwenden, wann immer Sie diese Funktionalität benötigen. – user2357112

Antwort

0

Leider gibt es keine eingebaute das tut es. Sie können es selbst mit Klassen implementieren, aber ein Paket namens cotoolz implementiert eine map() Funktion, die genau das tut.

Ihre Map-Funktion ist 4 mal langsamer als die eingebaute map(), aber es ist bewusst, das Generator-Protokoll und schneller als eine ähnliche Python-Implementierung (es ist in C geschrieben und erfordert einen C99-Compiler).

Ein Beispiel von ihrer Seite:

>>> 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

Haben parse_foo und parse_bar die Erweiterungen hinzufügen:

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

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

Oder codieren es nur in jeder Funktion:

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

Dies bricht 'send' und' throw'. – user2357112

+0

Das funktioniert nicht und bricht tatsächlich 'send' und' throw'. – Bharel

+0

@ user2357112 Auf welche Art und Weise bricht das "Senden" und "Werfen"? –

2

Es gibt einen anderen Weg, dies neben try ... yield ... except tun: durch einen neuen Generator zu implementieren. Mit dieser Klasse können Sie alle Ein- und Ausgänge des zugrunde liegenden Generator verwandeln:

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

Verbrauch:

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: Sie könnten von collections.Iterator erben wollen, aber es ist nicht notwendig in dieser Anwendungsfall.

+0

Vielen Dank für die Zeit, die Sie mir antworteten. Ich habe ein Paket namens [cotoolz] (https://pypi.python.org/pypi/cotoolz) gefunden, das genau das tut, aber es ist in C implementiert, was zu einer schnelleren Ausführung führt, also werde ich damit fortfahren. – Bharel