デコレータのテーマはHabréで何度も議論されてきましたが、私の意見では、この記事( stackoverflowに関する 1つの質問から発展しました)はこのトピックを最も明確に説明し、重要なことに、デコレータの使用に関する「ステップバイステップガイド」であり、初心者がすぐにこのテクニックを習得できるようにしますまともなレベル。
では、デコレータとは何ですか?
かなり長い記事が先にありますので、誰かが急いでいる場合、ここにデコレータの仕組みの例を示します:
def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello habr" print hello() ## <b><i>hello habr</i></b>
長い時間を過ごしたい人は、長い投稿を読んでください。
Pythonの関数はオブジェクトです
デコレータがどのように機能するかを理解するには、まずPythonの関数もオブジェクトであることに気付く必要があります。
これから何が続くか見てみましょう:
def shout(word=""): return word.capitalize()+"!" print shout() # : '!' # - , , # scream = shout # , : "shout", # "scream". , # "shout" "scream": print scream() # : '!' # , , "shout", # "scream" del shout try: print shout() except NameError, e: print e #: "name 'shout' is not defined" print scream() # : '!'
この事実を思い出してみましょう、すぐに戻りますが、さらに、Pythonの関数は別の関数内で定義できることを理解する価値があります。
def talk(): # "talk" ... def whisper(word=""): return word.lower()+"..."; # ... ! print whisper() # , "talk", # "whisper". talk() # : "..." # "talk" "whisper": try: print whisper() except NameError, e: print e # : "name 'whisper' is not defined"
機能リンク
さて、あなたはまだここにいますか?:)
これで、関数が本格的なオブジェクトであることがわかりました。つまり、次のことを意味します。
- 変数に関連付けることができます。
- 互いに内側に定義できます。
つまり、ある関数が別の関数を返すことができるということです!
見てみましょう:
def getTalk(type="shout"): # def shout(word=""): return word.capitalize()+"!" def whisper(word="") : return word.lower()+"..."; # if type == "shout": # , "()", , # return shout else: return whisper # ? # talk = getTalk() # , "talk" - "function": print talk # : <function shout at 0xb7ea817c> # , , " ": print talk() # - : print getTalk("whisper")() # : ...
関数を返すことができるので、それをパラメーターとして別の関数に渡すことができます。
def doSomethingBefore(func): print " - , , " print func() doSomethingBefore(scream) #: # - , , # !
さて、デコレータがどのように機能するかを理解するために必要な知識はすべて揃っています。
ご想像のとおり、デコレータは、実際には、装飾された関数が変更せずに行うことの前後に何かを行う機会を与える一種の「ラッパー」 にすぎません。
デコレータを「手動で」作成しましょう
# - , def my_shiny_new_decorator(a_function_to_decorate): # -"". # ( ?..) , # . def the_wrapper_around_the_original_function(): # , # print " - , " # a_function_to_decorate() # , # print " - , " # "a_function_to_decorate" # , -, # , , . # ! return the_wrapper_around_the_original_function # , , . def a_stand_alone_function(): print " , ?.." a_stand_alone_function() # : , ?.. # , , , # , , # , , : a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function) a_stand_alone_function_decorated() #: # - , # , ?.. # - ,
おそらく今度は、 a_stand_alone_functionの呼び出し中に、 代わりに a_stand_alone_function _decoratedが呼び出されるようにします。 簡単なことはありません。a_stand_alone_functionをmy_shiny_new_decoratorが返した関数で書き換えるだけです:
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function) a_stand_alone_function() #: # - , # , ?.. # - ,
これは@デコレータとまったく同じことだと既に推測しました。
デコレータの周りのミステリーのハローを破壊する
これは、デコレータ構文を使用して前の例を書く方法です。
@my_shiny_new_decorator def another_stand_alone_function(): print " " another_stand_alone_function() #: # - , # # - ,
はい、すべてが本当に簡単です! デコレータは、ビュー構造の構文糖衣です。
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
デコレータは、 デコレータデザインパターンの単なるPythonの実装です。 Pythonには、この記事で説明したデコレータやPaytonistに馴染みのあるイテレータなど、いくつかの古典的なデザインパターンが含まれています 。
もちろん、次のように、デコレータを相互にネストできます。
def bread(func): def wrapper(): print "</------\>" func() print "<\______/>" return wrapper def ingredients(func): def wrapper(): print "##" func() print "~~" return wrapper def sandwich(food="----"): print food sandwich() #: ---- sandwich = bread(ingredients(sandwich)) sandwich() #: # </------\> # ## # ---- # ~~ # <\______/>
そして、デコレータ構文を使用します:
@bread @ingredients def sandwich(food="----"): print food sandwich() #: # </------\> # ## # ---- # ~~ # <\______/>
装飾の順序は重要であることに注意してください:
@ingredients @bread def sandwich(food="----"): print food sandwich() #: # ## # </------\> # ---- # <\______/> # ~~
この時点で、デコレータとは何か、彼らは何を食べているかを理解していることに気付いて、喜んで去ることができます。
脳をもう少し苦しめたい人のために、明日はデコレータの高度な使用に関する記事の第2部を再翻訳します。
内容:
- ステップ1.デコレーターの基本的な理解 (この記事を読むだけです)
- ステップ2.デコレーターの高度な使用
ご注意 翻訳者:
ご清聴ありがとうございました。 翻訳とデザインに関するコメントに感謝します。翻訳の第2部ではそれらすべてを考慮に入れようとします。
UPD:2番目の部分では、装飾された関数への引数の受け渡し、メソッドの装飾、パラメーター付きの装飾子などの質問があります。
UPD2: 記事の2番目の部分が掲載されています。