2016-07-08 10 views
1

私がしたいと思います:この例ではPythonのコンテキストマネージャデコレータする(および逆)

# Simple example, one could replace try/except by any other nested construct 
def mycontextmanager_generator(foo): 
    try: 
     yield 
    except: 
     print 'bar' 
   raise 

mycontextmanager = build_contextmanager(mycontextmanager_generator) 
mydecorator = build_decorator(mycontextmanager_generator) 


>>> with mycontextmanager(): 
>>>  raise Exception('baz gone bar in context manager') 
... bar 


>>> @mydecorator() 
>>> def bazzer(): 
>>>  raise Exception('baz gone bar in decorator') 
>>> bazzer() 
... bar 

は、Iが生成機能からコンテキストマネージャを構築し、同じ機能のデコレータ。これは私が失敗した方法で達成しようとしたものです。私が欲しいものより一般的

は、DRYになることです:書き込みがtry/exceptブロックたら、再度デコレータコンテキストマネージャ両方を通してそれを再使用:試みを書き込むことによって/圏除いて一度だけを、ジェネレータ関数またはその他のラッパーに関係なく使用できます。

(PY2でPY3/contextlib2contextlib中)ContextDecorator事はクラスでのみ使用可能ですが、その場合には無用のようですが...私は何かが足りないのですか? __enter____exit__を使用して、クラスベースのContextManagerでtry/exceptブロックを実装する方法はありますか?

yield構文で構築されたコンテキストマネージャをデコレータに変換する可能性はありますか?

または逆の(デコレータからコンテキストマネージャ)?

もしそうでない場合は、その点に関してPythonの制限が何であるかを知っておきたいと思います。

私の理解では、yieldの構文はPythonインタプリタとコンテキストの切り替えに非常に厳密に縛られており、その点でその動作を変更できるかどうかはわかりません。

+0

だから、あなたが上記のケースを 'yield'を使用したいが、まだ例外を除いて、正しいですか? – syntonym

+1

あなたが何を求めているのか分かりません。あなたは外部ライブラリを参照しているように見えますが、私はあなたが意味するものを知っていると思いますが、私はここでもう一度推測しています(https://pypi.python.org/pypi/contextdecorator)。どのライブラリを使用しているか、どのバージョンのPythonとより具体的な動作の例(サンプル/擬似コード、期待/望ましい出力)を記述できますか? – Dunes

+0

@Dunesはpy3で 'contextlib'(これはデコレータやコンテキストマネージャーで遊んでいる人は知っているだろうと思っていました)ですが、実際にはもっと明確にするために質問を更新するつもりです – lajarre

答えて

1

contextmanagerがそのコンテキスト(_GeneratorContextManager)とContextDecoratorクラスの管理に使用するクラスを組み合わせることで、必要なものを簡単に達成できます。例えば。

from contextlib import ContextDecorator, _GeneratorContextManager 
from functools import wraps 

class MyContextManager(_GeneratorContextManager, ContextDecorator): 
    pass 

def contextmanager(func): 
    @wraps(func) 
    def helper(*args, **kwds): 
     return MyContextManager(func, args, kwds) 
    return helper 

@contextmanager 
def print_bar_on_error(): 
    try: 
     yield 
    except: 
     print('bar') 
     raise 

with print_bar_on_error(): 
    raise Exception('baz gone bar in context manager') 

は生成します。

bar 
Traceback (most recent call last): 
    File "run.py", line 28, in <module> 
    raise Exception('baz gone bar in context manager') 
Exception: baz gone bar in context manager 

とデコレータとして

@print_bar_on_error() 
def bazzer(): 
    raise Exception('baz gone bar in decorator') 
bazzer() 

を使用し生成します

bar 
Traceback (most recent call last): 
    File "run.py", line 32, in <module> 
    bazzer() 
    File "c:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\contextlib.py", line 30, in inner 
    return func(*args, **kwds) 
    File "run.py", line 31, in bazzer 
    raise Exception('baz gone bar in decorator') 
Exception: baz gone bar in decorator 
    return func(*args, **kwds) 
Exception: baz gone bar in decorator 
+0

実際には、私は 'ContextDecorator'を正しく見ていませんでした。 contextlibが何をしているのか分かりましたので、私にとってはそれほどシンプルではないようです。代替ソリューションを投稿しています(使用するのが面倒ですが、理解が簡単です) – lajarre

0

ないとはいえ、砂丘よりもソリューションを理解することが単純を活用する二重構文。

import contextlib 
import functools 

def handler(): 
    try: 
     yield 
    except: 
     print 'bar' 


my_contextmanager = contextlib.contextmanager(handler) 


def my_decorator(func): 
    @functools.wraps(func) 
    def wrapper(*args, **kwargs): 
     with my_contextmanager(): 
      func(*args, **kwargs) 
    return wrapper 


with my_contextmanager(): 
    raise Exception('baz') 

@my_decorator 
def f(): 
    raise Exception('baz') 

f() 

ができます:

bar 
bar 
関連する問題