Pythonデコレータの一般的な用途は何ですか?[閉まっている]


335

私は自分を適度に有能なPythonコーダーと考えるのが好きですが、私が理解できなかった言語の1つの側面はデコレーターです。

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

それで、皆さんが自分のプログラムでデコレーターを使用した例をいくつか提供できるかどうか疑問に思っています。うまくいけば、「A-ha!」瞬間とそれらを取得します。


5
また、デコレータはメモ化にも役立ちます。つまり、関数の計算が遅い結果をキャッシュします。デコレータは、入力をチェックする関数を返すことができ、それらがすでに提示されている場合は、キャッシュされた結果を返します。
Peter

1
Pythonには組み込みのデコレータがあることに注意してくださいfunctools.lru_cache。これは、2011
実行します。– Taegyung

Pythonデコレータライブラリのコンテンツは、それらの他の使用法の良いアイデアをあなたに与えるでしょう。
martineau

回答:


126

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

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):
    ...

13
Unixでは、time.clock()CPU時間を測定します。実time.time()時間を測定したい場合は、代わりに使用することもできます。
Jabba 2013

20
素晴らしい例!それが何をしているのかわからない。あなたがそこで何をしているのか、そしてデコレータがどのように問題を解決するのかについての説明はとても素晴らしいでしょう。
MeLight 2014年

7
まあ、それはmyFunction実行するのにかかる時間を測定します...
RSabet

98

同期に使用しました。

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()を置くだけです。


17
おそらく正当化されますが、デコレータは本質的に混乱します。あなたの後ろに来てあなたのコードを改造しようとする初年度の初心者に。簡単にこれを避けてください:do_something()にコードを「with lock:」の下のブロックで囲ませるだけで、誰もがあなたの目的を明確に見ることができます。デコレーターはスマートになりたい(実際には多くの人がそうである)人々によって非常に過剰に使用されていますが、その場合、コードは単なる人間になり、効果を発揮します。
Kevin J. Rice

18
@ KevinJ.Rice「初年度の初心者」がコードをよりよく理解できるようにコードを制約することは、ひどい習慣です。デコレータの構文は読みやすく、コードが大幅に分離されています。
TaylerJones、2015

18
@TaylerJones、コードの読みやすさは、書くときの私の最優先事項です。コードは変更されるたびに7回以上読み取られます。コードを理解するのが難しい(初心者や時間のプレッシャーの下で作業しているエキスパートにとって)は、誰かがソースツリーにアクセスするたびに支払わなければならない技術的負債です。
ケビンJ.ライス

@TaylerJonesプログラマーにとって最も重要なタスクの1つは、明快さを提供することです。
JDOaktown

70

いくつかの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()私のためにすべての仕事をします。


15
興味がある人のための実装があります@acceptsPEP 318に
マーティ

2
タイプミスがあると思います..最初のメソッドは
acceptである

1
@DevCいいえ、タイプミスのようには見えません。これは明らかに「accepts(..)」の実装ではないため、ここでは「accepts(..)」は、「myMethod(..)」の先頭の2行で実行されるはずの処理を実行します。これが適合する解釈のみ。
Evgeni Sergeev 2015年

1
バンプについて申し訳ありませんが、渡された引数のタイプをチェックしてTypeErrorを発生させることは、フロートのみをチェックする場合、たとえばintを受け入れないため、それが悪い習慣と見なされることを指摘したかっただけです。コード自体は、最大限の柔軟性を得るために、渡されるさまざまな種類の値に適応する必要があります。
Gustavo6046

2
Pythonで型チェックを行うための推奨される方法は、組み込みisinstance()関数を使用する方法です。これは、デコレータのPEP 318 実装で行われるためです。classinfo引数は1つ以上のタイプになる可能性があるため、これを使用すると@ Gustavo6046の(有効な)異議も軽減されます。PythonにもNumber抽象基本クラスがあるため、などの非常に一般的なテストisinstance(42, numbers.Number)が可能です。
martineau

48

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

Djangoはこれらを使用して、ビュー関数の「ログインが必要な」機能をラップしたり、フィルタ関数登録したりします

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

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

また、Python-Devニュースグループには、PEP 318(関数とメソッドのデコレータ)によって指摘されたユースケースに関する議論もあります。


Cherrypyは@ cherrypy.exposeを使用して、公開されている関数と非表示になっている関数を区別します。それが初めての紹介で、そこで慣れました。
Marc Maxmeister、2015年

26

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

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

23

Twistedライブラリは、ジェネレーターと組み合わせたデコレーターを使用して、非同期関数が同期しているように見せます。例えば:

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

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


14

もちろん、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()

10

私は主にデバッグ(引数と結果を出力する関数のラッパー)と検証(たとえば、引数が正しいタイプかどうかを確認するため、またはWebアプリケーションの場合、ユーザーが特定の方法)。


6

関数をスレッドセーフにするために次のデコレータを使用しています。コードが読みやすくなります。これは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

1
これは、そのように装飾された各関数に独自のロックがあることを意味しますか?
2010年

1
@grieveはい、デコレータが使用される(呼び出される)たびに、デコレートされる関数/メソッドの新しいロックオブジェクトが作成されます。
martineau

5
それは本当に危険です。inc_var()メソッドは、一度に1人しか呼び出せないという点で「スレッドセーフ」です。つまり、メソッドはメンバー変数「var」で動作し、おそらく他のメソッドもメンバー変数「var」で動作する可能性があり、ロックは共有されないため、これらのアクセスはスレッドセーフではありません。このようにすると、クラスXのユーザーに誤った安心感が与えられます。
Bob Van Zant

シングルロックが使用されるまで、スレッドセーフではありません。
Chandu

5

デコレータは、関数のプロパティを定義するため、または関数を変更するボイラープレートとして使用されます。完全に異なる関数を返すことは可能ですが直感に反します。ここで他の応答を見ると、最も一般的な用途の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())

本当じゃない。デコレータは、関数の動作を完全に変更できます。
再帰

はい。しかし、デコレータはどのくらいの頻度で「関数の動作を完全に変更する」のでしょうか。私が見たところ、プロパティの指定に使用されていない場合は、ボイラープレートコードに使用されているだけです。返信を編集しました。
Nikhil Chelliah、2009年

5

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


1

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

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

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

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

6
あなたの例をありがとう、しかし(apolgies)私はWTFを言わなければなりません-なぜこれを使うのですか?それは人々を混乱させる大きな可能性を秘めています。もちろん、私はエッジケースの使用のニーズを尊重しますが、あなたは多くの経験の浅いPython開発者が抱えている一般的な問題にぶつかっています-クラスを十分に使用していません。つまり、countの単純なクラスvarを用意し、初期化して使用するだけです。Noobはドロップスルー(非クラスベースのコード)を記述し、手の込んだ回避策でクラス機能の欠如に対処しようとする傾向があります。しないでください?お願いします?ハープ申し訳ありませんが、あなたの答えをありがとう、しかしあなたは私のためにホットボタンを押しました。
Kevin J. Rice 14年

コードレビューのプルリクエストとして表示された場合、私はこれで-1となるので、これも良いpythonで-1です。
Techdragon 2017年

可愛い。ばかげているが、かわいい。:)たまにある関数属性は気にしませんが、それらは典型的なPythonコードでは非常にまれなので、これを使用する場合は、デコレータの下に隠すのではなく、明示的に使用します。
PM 2Ring
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.