2016-11-08 3 views
1

私は次のスタイルで書かれた既存のコードをいくつか持っていて、期待どおりに動作します。私はコンテキストマネージャーとしてchunkUndoを使用できます。私は上記のように、すでに書かれたコンテキストマネージャのコードの多くを持っているのでPython 2.7でクラスを使わずにデコレータとしてコンテキストマネージャを作る方法は?

from contextlib import contextmanager 
from functools import wraps 

@contextmanager 
def chunkUndo(mode='simple'): 
    print 'mode:',mode 
    print 'undo chunk start' 
    try: 
     yield 
    finally: 
     print 'undo chunk end' 

# this works 
with chunkUndo(): 
    print 'do work here' 

しかし、私はclass based contextDecoratorsにそれらを変更したくありません。代わりに、私はすでに、それデコレータ機能のようなものを作るためにchunkUndo装飾するためにデコレータを追加したい:

def makeContextDecorator(f): 

    @wraps(f) 
    def wrapper(*args, **kw): 
     # some code here 
    return wrapper 

@makeContextDecorator 
@contextmanager 
def chunkUndo(mode='simple'): 
    print 'mode:',mode 
    print 'undo chunk start' 
    try: 
     yield 
    finally: 
     print 'undo chunk end' 

@chunkUndo 
def do_work(): 
    print 'do work' 

do_work() 

最終的な結果は、私はおそらく同様にそれに引数を渡し、デコレータとコンテキストマネージャとしてchunkUndoの両方を使用することができますです。それ、どうやったら出来るの ?

+0

下のコードで動作するように変更した後でも、 'chunkUndo'が(' with'文の)先頭のコードと同じように動作するように正しく理解していますか?私は、あなたの 'makeContextDecorator'関数がその場合にはもっと洗練されたものでなければならないと思っています(単なる関数ではなくクラスかもしれません)。 – Blckknght

+0

はい私は上記のコードはまだ動作します。目的は、 'chunkUndo'を' context manager'と 'decorator'として動作させることですが、クラスを使用することはありません。 – Shuman

+1

なぜあなたはクラスを使わないでそれをする必要がありますか?私は、次の男ほど精神的なレベルの爪で叩きつけるのが大好きですが、ある時点では、言語があなたにそれをやりたいと望むようにする必要があります。 – kindall

答えて

0

chunkUndoがコンテキストマネージャとして機能し、飾る機能に自分自身を適用するデコレータとして機能させたい場合は、makeContextDecorator関数をもっと洗練されたものにする必要があります。ここにあなたの例の両方で動作します最初の試みです:

from functools import wraps 

def makeContextDecorator(cm): 
    def wrapper(func=None): 
     if func is not None: 
      @wraps(func) 
      def inner(*args, **kwargs): 
       with cm(): 
        return func(*args, **kwargs) 
      return inner 
     else: 
      return cm() 
    return wrapper 

はここでそれが行動にどのように見えるかです:

@makeContextDecorator 
@contextlib.contextmanager 
def foo(): 
    print("start") 
    try: 
     yield 
    finally: 
     print("end") 

with foo(): 
    print("with") # prints "start", "with", "end" on separate lines 

@foo 
def bar(x): 
    print("bar", x) 

bar(1) # prints "start", "bar 1", "end" on separate lines 

この設計では、唯一の引数を取ることはありませんコンテキストマネージャで動作します。

あなたは、それは(あなただけ*args**kwargsスタイルの引数を受け入れるようにwrapperを変更する必要があると思います)引数付きwith文で動作させることができますが、呼ば取得の違いを見分けることができないので、それは少し厄介になります(例えばwith foo(lambda x: x*2):)を呼び出し、デコレータとして呼び出されます。

デコレータ構文を使用しているときにコンテキストマネージャの引数を受け入れることは、より難しくなります。これは、引数を持つデコレータ(たとえば@foo("xyz"))を呼び出すと、デコレータが実際にデコレータファクトリであることを意味します。デコレータ(別の関数を変更する関数)のように動作するものを返す必要があります。しかし、戻り値はコンテキストマネージャのように直接動作する必要があります。あなたは一度に両方を行うことができるクラスを書く必要があります。これは、あなたが避けたいと言っていたものです。

関連する問題