2016-04-12 6 views
7

ですが、私はこれらのパーサーを持っているとしましょう:変更歩留まりのからの戻り値

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

parse_fooparse_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: ...のような醜い何かを行い、例外を渡すことです。それは醜い、厄介でバグの多い傾向があります。

+0

私はあなたの最善の策は、おそらく発電機に至る 'send'と' throw'を通過する間に、 'map'が何をし' passthrough_map'を実装することだと思いますあなたはマッピングしています。 IIRCは、その権利を行うことは難しいですが、一度正しく取得する必要があるだけで、その機能が必要なときはいつでも再利用できます。 – user2357112

答えて

0

残念ながらを内蔵していません。クラスを使用して自分で実装することもできますが、cotoolzというパッケージは、まったく同じ機能を実装するmap()を実装しています。

map関数は組み込みのmap()よりも4倍遅いですが、ジェネレータプロトコルを認識していて、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 
0

parse_fooparse_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" 
+0

これは 'send'と' throw'を破ります。 – user2357112

+0

これは動作せず、実際に 'send'と' throw'を破ります。 – Bharel

+0

@ user2357112これはどのようにして 'send'と' throw'を破りますか? –

2

はほか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 

EDIT:あなたはcollections.Iteratorを継承する場合がありますが、それはでは必要ありませんですこのユースケース。

+0

あなたは私に答える時間のために非常に感謝します。私は[cotoolz](https://pypi.python.org/pypi/cotoolz)というパッケージを見つけましたが、これは明らかに正確に実行されますが、Cで実装されているため、より高速な実行が可能です。 – Bharel

関連する問題