web-dev-qa-db-ja.com

Pythonデコレータの一般的な用途は何ですか?

私は自分を適度な能力を持つPythonコーダーと考えるのが好きですが、私が決して理解できない言語の1つの側面はデコレータです。

私はそれらが(表面的に)知っており、チュートリアル、例、Stack Overflowに関する質問を読んでおり、構文を理解し、自分で書くことができ、時々@classmethodと@staticmethodを使用しますが、自分のPythonコードの問題を解決するデコレーター。 「うーん...これはデコレータの仕事のように見える!」と思う問題に遭遇することはありません。

それで、皆さんがあなた自身のプログラムでデコレーターを使用した例のいくつかを提供してくれるかもしれないと思っています。そして、できれば「A-ha!」があるでしょう。瞬間とgetそれら。

325
Dana

主にタイミングの目的でデコレータを使用しています

def time_dec(func):

  def wrapper(*arg):
      t = time.clock()
      res = func(*arg)
      print func.func_name, time.clock()-t
      return res

  return wrapper


@time_dec
def myFunction(n):
    ...
122
RSabet

それらを同期に使用しました。

import functools

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(f):
        @functools.wraps(f)
        def newFunction(*args, **kw):
            lock.acquire()
            try:
                return f(*args, **kw)
            finally:
                lock.release()
        return newFunction
    return wrap

コメントで指摘したように、Python 2.5以降では、 with ステートメントを threading.Lock (または multiprocessing.Lock バージョン2.6以降)オブジェクトは、デコレータの実装を次のように単純化します。

import functools

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(f):
        @functools.wraps(f)
        def newFunction(*args, **kw):
            with lock:
                return f(*args, **kw)
        return newFunction
    return wrap

とにかく、次のように使用します。

import threading
lock = threading.Lock()

@synchronized(lock)
def do_something():
  # etc

@synchronzied(lock)
def do_something_else():
  # etc

基本的には、関数呼び出しの両側にlock.acquire()/lock.release()を置くだけです。

96
John Fouhy

RMIを介してPythonメソッドに渡される型チェックパラメーターにデコレーターを使用します。したがって、同じパラメーターのカウントを繰り返す代わりに、例外を発生させるmumbo-jumboを何度も繰り返します

def myMethod(ID, name):
    if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):
        raise BlaBlaException() ...

私はただ宣言します

@accepts(uint, utf8string)
def myMethod(ID, name):
    ...

そしてaccepts()がすべての仕事をしてくれます。

66
Simon

デコレータは、追加機能で透過的に「ラップ」したいものすべてに使用されます。

Djangoは、 「ビュー関数でのログインが必要」機能 のラッピング、および フィルター関数の登録 にこれらを使用します。

名前付きログをクラスに追加する にはクラスデコレータを使用できます。

既存のクラスまたは関数の動作に「タック」できる十分に汎用的な機能は、装飾のための公平なゲームです。

また、 PEP 318-関数とメソッドのデコレータ が指す Python-Devニュースグループに関するユースケースの議論 があります。

47
cdleary

Twistedライブラリーは、ジェネレーターと組み合わされたデコレーターを使用して、非同期関数が同期的であるという錯覚を与えます。例えば:

@inlineCallbacks
def asyncf():
    doStuff()
    yield someAsynchronousCall()
    doStuff()
    yield someAsynchronousCall()
    doStuff()

これを使用すると、多数の小さなコールバック関数に分割されるコードを1つのブロックとして非常に自然に記述できるため、理解と保守がはるかに容易になります。

23
DNS

ノーズテストでは、いくつかのパラメーターセットを持つ単体テスト関数またはメソッドを提供するデコレーターを作成できます。

@parameters(
   (2, 4, 6),
   (5, 6, 11),
)
def test_add(a, b, expected):
    assert a + b == expected
23
Torsten Marek

明らかな用途の1つは、もちろんロギングです。

import functools

def log(logger, level='info'):
    def log_decorator(fn):
        @functools.wraps(fn)
        def wrapper(*a, **kwa):
            getattr(logger, level)(fn.__name__)
            return fn(*a, **kwa)
        return wrapper
    return log_decorator

# later that day ...
@log(logging.getLogger('main'), level='warning')
def potentially_dangerous_function(times):
    for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()
12
MisterMetaphor

主にデバッグ(引数と結果を出力する関数のラッパー)と検証(引数が正しい型であるか、Webアプリケーションの場合、ユーザーが特定の方法)。

8
DzinX

関数をスレッドセーフにするために、次のデコレータを使用しています。コードが読みやすくなります。 John Fouhyによって提案されたものとほとんど同じですが、違いは1つの関数で機能することと、ロックオブジェクトを明示的に作成する必要がないことです。

def threadsafe_function(fn):
    """decorator making sure that the decorated function is thread safe"""
    lock = threading.Lock()
    def new(*args, **kwargs):
        lock.acquire()
        try:
            r = fn(*args, **kwargs)
        except Exception as e:
            raise e
        finally:
            lock.release()
        return r
    return new

class X:
    var = 0

    @threadsafe_function     
    def inc_var(self):
        X.var += 1    
        return X.var
6
luc

デコレータは、関数のプロパティを定義するか、それを変更する定型句として使用されます。完全に異なる関数を返すことは可能ですが、直感に反します。ここの他の応答を見ると、最も一般的な用途の1つは、ロギング、プロファイリング、セキュリティチェックなど、他のプロセスの範囲を制限することです。

CherryPyはオブジェクトディスパッチを使用して、URLとオブジェクト、そして最終的にはメソッドを照合します。これらのメソッドのデコレーターは、CherryPyがこれらのメソッドを使用することが許可されているか許可されているかを示します。たとえば、 チュートリアル から適応:

class HelloWorld:

    ...

    def secret(self):
        return "You shouldn't be here."

    @cherrypy.expose
    def index(self):
        return "Hello world!"

cherrypy.quickstart(HelloWorld())
5
Nikhil Chelliah

私は最近、ソーシャルネットワーキングWebアプリケーションの作業中にそれらを使用しました。コミュニティ/グループの場合、新しいディスカッションを作成し、その特定のグループのメンバーである必要があるメッセージに返信するために、メンバーシップの承認を与えることになっています。そこで、デコレータ@membership_requiredを作成し、必要な場所に配置しました。

5
aatifh

デコレータを使用して、関数メソッド変数を簡単に作成できます。

def static_var(varname, value):
    '''
    Decorator to create a static variable for the specified function
    @param varname: static variable name
    @param value: initial value for the variable
    '''
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate

@static_var("count", 0)
def mainCallCount():
    mainCallCount.count += 1
1
user1476056

このデコレータを使用してパラメータを修正します

def fill_it(arg):
    if isinstance(arg, int):
        return "wan" + str(arg)
    else:
        try:
            # number present as string
            if str(int(arg)) == arg:
                return "wan" + arg
            else:
                # This should never happened
                raise Exception("I dont know this " + arg)
                print "What arg?"
        except ValueError, e:
            return arg

def fill_wanname(func):
    def wrapper(arg):
        filled = fill_it(arg)
        return func(filled)
    return wrapper

@fill_wanname
def get_iface_of(wanname):
    global __iface_config__
    return __iface_config__[wanname]['iface']

これは、いくつかの関数をリファクタリングして引数「wanN」を渡す必要があるときに書かれましたが、古いコードでは、Nまたは「N」のみを渡しました

1
HVNSweeting