2016-04-12 23 views
7

Diciamo che ho questi parser:Modifica resa dal valore di ritorno di

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

parse_foo e parse_bar sono entrambi i generatori che producono file uno a uno. Se voglio creare una singola funzione spedizione, farei questo:

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

La resa dalla sintassi mi permette di informazioni sul tunnel facilmente su e giù per i generatori.

C'è un modo per mantenere il tunneling mentre si modificano i risultati di rendimento?
In questo modo, mentre la rottura del tunnel è facile:

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

Ma in questo modo non posso usare .send() o .throw() fino al parser.

L'unico modo che sto pensando è di fare qualcosa di brutto come try: ... except Exception: ... e passare le eccezioni, mentre si fa lo stesso per .send(). È brutto, disordinato e incline agli insetti.

+0

penso che la soluzione migliore sarebbe probabilmente per implementare un 'passthrough_map' che fa quello' map' fa passando '' send' e throw' attraverso al generatore stai mappando. IIRC, farlo correttamente è complicato, ma devi solo farlo bene una volta, e poi puoi riutilizzarlo ogni volta che ti serve quella funzionalità. – user2357112

risposta

0

Sfortunatamente non esiste un built-in che lo sia. Puoi implementarlo tu stesso usando le classi ma un pacchetto chiamato cotoolz implementa una funzione map() che fa esattamente questo.

La loro funzione di mappa è 4 volte più lenta del built-in map() ma è consapevole del protocollo del generatore e più veloce di un'implementazione Python simile (è scritta in C e richiede un compilatore C99).

Un esempio dalla loro pagina:

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

Avere parse_foo e parse_bar aggiungere le estensioni:

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

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

O semplicemente hardcode in ogni funzione:

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

Interrompe 'send' e' throw'. – user2357112

+0

Questo non funziona e infatti interrompe 'send' e' throw'. – Bharel

+0

@ user2357112 In che modo questa interruzione 'send' e' throw'? –

2

C'è un altro modo di fare questo oltre try ... yield ... except: mediante l'attuazione di un nuovo generatore. Con questa classe è possibile trasformare tutti gli ingressi e le uscite del generatore di fondo:

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

Usage:

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: Si potrebbe desiderare di ereditare da collections.Iterator, ma non è neccessary in questo caso.

+0

Grazie mille per il tempo che mi hai dedicato a rispondere. Ho trovato un pacchetto chiamato [cotoolz] (https://pypi.python.org/pypi/cotoolz) che apparentemente fa esattamente questo, ma è implementato in C, risultando in un'esecuzione più veloce, quindi vado con esso. – Bharel