Pythonのデコレータを段階的に理解しています。 ステップ2



そして再び、すべての読者に良い一日を!

翻訳の最初の部分に関心をお寄せいただきありがとうございます。2番目の部分も失望させないことを願っています。




そのため、この記事の最初の部分では、デコレーターとその作業の原理について基本的な知識を身に付け、独自に作成しました。

ただし、前に調べたすべてのデコレーターには、非常に重要な機能が1つもありませんでした-装飾される関数に引数を渡します。

さて、この誤解を修正してください!





装飾された関数に引数を渡す(「転送する」)



ブラックマジックはありません。実際に必要なのは、引数を渡すことだけです。

def a_decorator_passing_arguments(function_to_decorate): def a_wrapper_accepting_arguments(arg1, arg2): #    print ",   :", arg1, arg2 function_to_decorate(arg1, arg2) return a_wrapper_accepting_arguments # ,    ,   , #    "",         #      @a_decorator_passing_arguments def print_full_name(first_name, last_name): print " ", first_name, last_name print_full_name("", "") # : # ,   :   #     # *
      
      





*-注 翻訳者:ピーター・ベンクマン- 同じ名前のカルト映画の主人公、ゴーストバスターズの名前



ドレッシング方法



理解すべき重要な事実の1つは、Pythonの関数とメソッドはほとんど同じことです。ただし、メソッドは常に最初のパラメーターがオブジェクト自体( self )を参照することを期待しています。 これは、 selfを忘れることなく、関数と同じ方法でメソッドのデコレータを作成できることを意味します。

 def method_friendly_decorator(method_to_decorate): def wrapper(self, lie): lie = lie - 3 # ,  -     :-) return method_to_decorate(self, lie) return wrapper class Lucy(object): def __init__(self): self.age = 32 @method_friendly_decorator def sayYourAge(self, lie): print " %s,     ?" % (self.age + lie) l = Lucy() l.sayYourAge(-3) # :  26,     ?
      
      





もちろん、最も一般的なデコレータを作成し、それを任意の関数またはメソッドに適用したい場合、 * argsargsリストをアンパックし、 ** kwargskwargs辞書をアンパックするという事実を使用する価値があります。

 def a_decorator_passing_arbitrary_arguments(function_to_decorate): #  ""    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs): print "   -?:" print args print kwargs #    *args  **kwargs #        ,    : # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/ function_to_decorate(*args, **kwargs) return a_wrapper_accepting_arbitrary_arguments @a_decorator_passing_arbitrary_arguments def function_with_no_argument(): print "Python is cool, no argument here." #   ,   :) function_with_no_argument() # : #    -?: # () # {} # Python is cool, no argument here. @a_decorator_passing_arbitrary_arguments def function_with_arguments(a, b, c): print a, b, c function_with_arguments(1,2,3) # : #    -?: # (1, 2, 3) # {} # 1 2 3 @a_decorator_passing_arbitrary_arguments def function_with_named_arguments(a, b, c, platypus=" ?"): print "  %s, %s  %s ? %s" %\ (a, b, c, platypus) function_with_named_arguments("", "", "", platypus="!") # : #    -?: # ('', '', '') # {'platypus': '!'} #   ,    ? ! class Mary(object): def __init__(self): self.age = 31 @a_decorator_passing_arbitrary_arguments def sayYourAge(self, lie=-3): #        print " %s,     ?" % (self.age + lie) m = Mary() m.sayYourAge() # : #    -?: # (<__main__ .Mary object at 0xb7d303ac>,) # {} #  28,     ?
      
      







さまざまな引数でデコレータを呼び出す



素晴らしい、それを整理しました。 異なる引数でデコレータを呼び出そうとすると、あなたは今何を言いますか?



これは見た目ほど単純ではありません。デコレータは関数を引数として受け入れる必要があり、他に何も渡すことができないためです。

そのため、ソリューションを紹介する前に、既にわかっていることをメモリで更新したいと思います。

 #  -    def my_decorator(func): print "  " def wrapper(): print " - ,  " func() return wrapper #  ,    ,   "@"-: def lazy_function(): print "zzzzzzzz" decorated_function = my_decorator(lazy_function) # :    #    "  ",     ,   : #  .   @my_decorator def lazy_function(): print "zzzzzzzz" # :   
      
      





ご覧のとおり、これらは2つの類似したアクションです。 書くとき
 @my_decorator
      
      



-インタプリタに「 my_decoratorという関数を呼び出す」ように伝えます。 これは重要なポイントです。なぜなら、この名前はどちらもデコレーターに直接つながる可能性があるからです。

怖いことをしましょう!:)

 def decorator_maker(): print "  !     : "+\ "      ." def my_decorator(func): print " - !     :    ." def wrapped(): print (" -    . " "         . " "     .") return func() print "   ." return wrapped print "  ." return my_decorator #    .        new_decorator = decorator_maker() # : #   !     :       . #   . #    def decorated_function(): print " -  ." decorated_function = new_decorator(decorated_function) # : #  - !     :    . #    . #    : decorated_function() # : #  -    .          . #      . #  -  .
      
      





長い? 長い。 中間変数を使用せずにこのコードを書き直します。

 def decorated_function(): print " -  ." decorated_function = decorator_maker()(decorated_function) # : #   !     :       . #   . #  - !     :    . #    . # : decorated_function() # : #  -    .          . #      . #  -  .
      
      





そして今、再び、さらに短く:

 @decorator_maker() def decorated_function(): print "I am the decorated function." # : #   !     :       . #   . #  - !     :    . #    . #  : decorated_function() # : #  -    .          . #      . #  -  .
      
      





「@」記号の後に関数を呼び出したことに気づきましたか?:)



最後に、デコレータの引数に戻りましょう。関数を使用してその場でデコレータを作成すれば、引数を渡すことができますよね?

 def decorator_maker_with_arguments(decorator_arg1, decorator_arg2): print "  !     :", decorator_arg1, decorator_arg2 def my_decorator(func): print " - .         :", decorator_arg1, decorator_arg2 #       ! def wrapped(function_arg1, function_arg2) : print (" -    .\n" "      : \n" "\t-  : {0} {1}\n" "\t-  : {2} {3}\n" "      " .format(decorator_arg1, decorator_arg2, function_arg1, function_arg2)) return func(function_arg1, function_arg2) return wrapped return my_decorator @decorator_maker_with_arguments("", "") def decorated_function_with_arguments(function_arg1, function_arg2): print (" -         : {0}" " {1}".format(function_arg1, function_arg2)) decorated_function_with_arguments("", "") # : #   !     :   #  - .         :   #  -    . #       : # -  :   # -  :   #        #  -         :  
      
      





*-注 翻訳者:この例では、著者は人気シリーズThe Big Bang Theoryの主人公の名前に言及しています。

ここに、任意の引数を渡すことができる人気のデコレータがあります。

もちろん、変数は引数にすることができます。

 c1 = "" c2 = "" @decorator_maker_with_arguments("", c1) def decorated_function_with_arguments(function_arg1, function_arg2): print (" -         : {0}" " {1}".format(function_arg1, function_arg2)) decorated_function_with_arguments(c2, "") # : #   !     :   #  - .         :   #  -    . #       : # -  :   # -  :   #        #  -         :  
      
      





したがって、通常の関数のように、デコレータに引数を渡すことができます。 必要に応じて、 * argsおよび** kwargsを使用してアンパックを使用できます。

ただし、デコレータは1回だけ呼び出されることに注意してください。 Pythonがスクリプトをインポートした瞬間。 その後、引数whichを変更できなくなります。

import x 」と記述すると、 xからのすべての関数 すぐ装飾され 、何も変更できなくなります。



少し練習:デコレータをデコレータで書く



あなたがこの時点まで読んで、まだランクにいる場合-ここに私からのボーナスがあります。

この小さなトリックにより、通常のデコレータを引数を取るデコレータに変えることができます。

最初に、引数を取るデコレータを取得するために、別の関数を使用して作成しました。

デコレータを包みました。

関数をラップするものはありますか?

まさに、デコレーター!



デコレータ用のデコレータを作成して楽しみましょう。

 def decorator_with_args(decorator_to_enhance): """        .     ,    .    .        ,       ,    ,  ,    . """ #     ,      : def decorator_maker(*args, **kwargs): #    ,      # ,    ,   "" def decorator_wrapper(func): #   ,     , ,    #   ( ). #    ,        # decorator(func, *args, **kwargs) # ,     return decorator_to_enhance(func, *args, **kwargs) return decorator_wrapper return decorator_maker
      
      





これは次のように使用できます。

 #   ,         :-) #   ,      "decorator(func, *args, **kwargs)" @decorator_with_args def decorated_decorator(func, *args, **kwargs): def wrapper(function_arg1, function_arg2): print "  ...:", args, kwargs return func(function_arg1, function_arg2) return wrapper #       ,   : @decorated_decorator(42, 404, 1024) def decorated_function(function_arg1, function_arg2): print "", function_arg1, function_arg2 decorated_function(" ", " ") # : #   ...: (42, 404, 1024) {} #      # !
      
      





今のあなたの気持ちを知っていると思います。

あなたがこの感覚を最後に経験したとき、彼らがあなたに言っていることを聞いて:「再帰を理解するには、まず再帰を理解しなければなりません。」

しかし今、あなたはあなたがそれを理解したことをうれしく思いますか?;)



デコレータの使用に関する推奨事項





最後の問題は、Python 2.5でfunctools.wrapsを含むfunctoolsモジュールを標準ライブラリに追加することで部分的に解決されました。これにより、ラップされる関数に関するすべての情報(名前、モジュール領域、docstringsなど)がラッパー関数にコピーされます。

楽しい事実は、 functools.wraps自体がデコレータであることです。

 #   ,     __name__ . def foo(): print "foo" print foo.__name__ # : foo # ,     : def bar(func): def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ # : wrapper # "functools"      import functools def bar(func): #  "wrapper"  "func" #   : @functools.wraps(func) def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ # : foo
      
      





デコレータはどのように使用できますか?



結論として、よく耳にする質問に答えたいと思います。なぜデコレータが必要なのですか? どのように使用できますか?

デコレータを使用して、サードパーティライブラリの関数の機能を拡張したり(変更できないコード)、デバッグを簡素化したりできます(まだ確立されていないコードを変更したくない)。

また、デコレータを使用して、同じコードを使用してさまざまな機能を拡張することも、毎回書き換えることなく便利です。たとえば、

 def benchmark(func): """ ,  ,     . """ import time def wrapper(*args, **kwargs): t = time.clock() res = func(*args, **kwargs) print func.__name__, time.clock() - t return res return wrapper def logging(func): """ ,   . (,    ,      !) """ def wrapper(*args, **kwargs): res = func(*args, **kwargs) print func.__name__, args, kwargs return res return wrapper def counter(func): """ ,       . """ def wrapper(*args, **kwargs): wrapper.count += 1 res = func(*args, **kwargs) print "{0}  : {1}x".format(func.__name__, wrapper.count) return res wrapper.count = 0 return wrapper @benchmark @logging @counter def reverse_string(string): return str(reversed(string)) print reverse_string("     ") print reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!") # : # reverse_string ('     ',) {} # wrapper 0.0 # reverse_string  : 1x #       # reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {} # wrapper 0.0 # reverse_string  : 2x # !amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A
      
      





したがって、デコレータは任意の関数に適用でき、その機能を拡張し、1行のコードを書き直す必要はありません!

 import httplib @benchmark @logging @counter def get_random_futurama_quote(): conn = httplib.HTTPConnection("slashdot.org:80") conn.request("HEAD", "/index.html") for key, value in conn.getresponse().getheaders(): if key.startswith("xb") or key.startswith("xf"): return value return ", ...  !" print get_random_futurama_quote() print get_random_futurama_quote() #outputs: #get_random_futurama_quote () {} #wrapper 0.02 #get_random_futurama_quote  : 1x #The laws of science be a harsh mistress. #get_random_futurama_quote () {} #wrapper 0.01 #get_random_futurama_quote  : 2x #Curse you, merciful Poseidon!
      
      





Pythonには、 property、staticmethodなどのデコレーターが含まれています。

Djangoでは、デコレータを使用して、キャッシュの制御、権限の制御、アドレスハンドラの定義を行います。 Twistedでは、偽の非同期インライン呼び出しを作成します。

デコレータは、実験のための最も広い範囲を開きます! そして、この記事がその開発に役立つことを願っています!

ご清聴ありがとうございました!



内容:



ご注意 翻訳者:

翻訳とデザインに関するコメントに感謝します。 記事の2番目の部分が、最初の部分よりも自明で有用なものになっていることを願っています。



All Articles