2016-04-12 23 views
7

Powiedzmy mam te parsery:modyfikując wydajność od za wartość zwracana

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

parse_foo i parse_bar są oba generatory, które dają rzędach jeden za drugim. Jeśli chcę, aby utworzyć pojedynczą funkcję wysyłki, chciałbym to zrobić:

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

Wydajność od składni pozwala mi informacji tunelu łatwo w górę iw dół generatorów.

Czy istnieje sposób na utrzymanie tunelowania podczas modyfikowania wyników?
przeciwnym razie podczas rozbijania tunelowanie jest proste:

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

Ale w ten sposób nie można użyć .send() lub .throw() całą drogę do parsera.

Jedyny sposób, w jaki myślę, to robienie czegoś brzydkiego, takiego jak try: ... except Exception: ... i przekazanie wyjątków w górę, robiąc to samo dla .send(). Jest brzydki, brudny i podatny na błędy.

+0

myślę najlepiej byłoby prawdopodobnie wdrożyć 'passthrough_map' że robi to, co robi' map' podczas przechodzenia 'send' i' throw' przez generator mapujesz. IIRC, wykonanie tego prawa jest trudne, ale potrzebujesz go tylko raz, a następnie możesz go użyć ponownie, gdy tylko potrzebujesz tej funkcjonalności. – user2357112

Odpowiedz

0

Niestety nie ma wbudowanego, że to robi. Możesz go zaimplementować samodzielnie przy użyciu klas, ale pakiet o nazwie cotoolz implementuje funkcję map(), która dokładnie to robi.

Ich funkcja mapy jest 4 razy wolniejsza niż wbudowana map(), ale jest świadoma protokołu generatora i szybsza niż podobna implementacja Pythona (jest napisana w języku C i wymaga kompilatora C99).

Przykład z ich strony:

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

Czy parse_foo i parse_bar dodać rozszerzenia:

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

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

Albo po prostu zakodować go w każdej funkcji:

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

Powoduje to przerwanie 'send' i' throw'. – user2357112

+0

To nie działa, aw rzeczy samej psuje 'send' i' throw'. – Bharel

+0

@ user2357112 W jaki sposób łamie 'send' i' throw'? –

2

Jest też inny sposób robi to oprócz try ... yield ... except: poprzez wdrożenie nowego generatora. Z tej klasy można przekształcić wszystkie wejścia i wyjścia swojej podstawowej generatora:

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

Zastosowanie:

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: Może chcesz dziedziczyć collections.Iterator, ale to nie jest konieczne w ten przypadek.

+0

Dziękuję bardzo za udzielenie mi odpowiedzi. Znalazłem pakiet o nazwie [cotoolz] (https://pypi.python.org/pypi/cotoolz), który najwyraźniej robi dokładnie to, ale jest zaimplementowany w C, co powoduje szybsze wykonanie, więc pójdę z nim. – Bharel