Pythonの美しさ--Decoratorの詳細(一)

30616 ワード

There should be one—--and preferably only one –--obvious way to do it.----The Zen of Python,by Time Petersより抜粋
いくつかの昔の事
正式にDecoratorの話題に入る前に、ちょっとした話をさせていただきます.最近のプロジェクト開発の過程で、ある同僚は私のコードを読んだとき、なぜ同じ検証コードがサービスインタフェースに繰り返されるのかと疑問を提起した.
あるサービスインタフェースがゲーム中のプレイヤーが建物を建てたい場合:

  
    
def build(user, build_name):
if not is_user_valid(user):
redirect(
" /auth/login/ " )
return False
# do something here
return create_building(build_name)

def build(user, build_name): if not is_user_valid(user): redirect("/auth/login/") return False #do something here return create_building(build_na
buildという方法では、最初の3行はプレイヤーが検証したかどうかを検出するために使用され、非検証のプレイヤーであれば、ログインページにリダイレクトします.検証されたプレイヤーなら、彼が望む建物を建ててあげます.
彼はこのような3行の検証コード(すでに短いが)がいずれかのプレイヤーサービスインタフェースに現れることを指摘した--建物をアップグレードしたり、建物を分解したり、頻繁にチャットしたりして、私はこれがCtrl+C、Ctrl+Vの時間だと言ったが、その時私は深く考えて、もし今需要が変動したら、元の検証に失敗した場合、ログを書く必要があると思っていた.元の3行のコードは4行になります.

   
     
if not is_user_valid(user):
redirect(
" /auth/login/ " )
log_warning(
" %s is trying to enter without permission! " % (user[ " name " ]))
return False

 
もっと苦しいのはif not isが現れるたびにuser_valid(user)の場所にlogを追加warningのこの仕事、これらの时間はあなたが良いお茶を入れて、1首の《Poker Face》を聞いて、tweetは何度も、甚だしきに至っては1つのバグを修復しました......
冒頭のTime Petersが言ったように、いつも最善の方法があります.その後頼総に教えてもらうと、デコルテが祭られます.以下は私がDecoratorに基づいて書き換えた検証コードです.

  
    
def authenticated(method):
def wrapper(user, * args):
if not is_user_valid(user):
redirect(
" /auth/login/ " )
log_warning(
" %s is trying to enter without permission! " % (user[ " name " ]))
return False
return method(user, * args)
return wrapper
@authenticated
def build(user, build_name):
return create_building(build_name)

 
 
 
Decoratorを使用するメリットは、プレイヤーサービスインタフェース--建物をアップグレードしたり、建物を分解したり、チャットしたりすることです.検証が必要な場合は、方法の前に@authenticatedを付けるだけでいいです.さらに重要なのは、必要に応じて検証に失敗した場合の処理(上記のlogなど)で、元のコードの構造に影響を与えることはありません.authenticated方法にlogを加えるだけでいいからです.warningの仕事はよく考えてみましょう.
間違いなくDecoratorはコード構造の維持に神のような役割を果たしている.以下、Decoratorの旅に入りましょう.PS:もしあなたがすでにDecoratorの達人であれば、まずCtrl+Wを焦らないでください.
 
 
初期Decorator
Decorator、修飾子、Python 2です.4に追加された機能はpythonerがメタプログラミングを実現する最新の方式であり、同時に最も簡単なメタプログラミング方式でもある.なぜ「一番簡単」なのか.はい、実はDecorator以前からclassmethod()とstaticmethod()に内蔵されていましたが、彼らの欠点は関数名の重複使用(David MertzのCharming Python: Decorators make magic easyを参照)を招くことです.以下は本人の原文から抜粋します:class C:def foo(cls,y):print"classmethod",cls,y foo=classmethod

  
    
class C:
def foo(cls, y):
print " classmethod " , cls, y
foo
= classmethod(foo)

はい、classmethodは関数変換だけをしていますが、fooという名前を2回追加しました.人間は怠惰で進歩したという言葉を覚えています.Decoratorの誕生は、fooを2回少なくした.

    
      
class C:
@classmethod
def foo(cls, y):
print " classmethod " , cls, y

読者はすでにDecoratorがpythonでどのように処理されているかを考えているかもしれません(まだ見当がつかない場合は、limodouが書いたDecorator学習ノートを見てみることを強くお勧めします).以下に4つの使い方を示します.
 
書き方
Decoratorの使用
Decoratorを使用しない
単一のDecorator、パラメータなし
@decdef method(args):    pass
def method(args):    passmethod = dec(method)
複数のDecorator、パラメータなし
@dec_a@dec_b@dec_cdef method(args):    pass
def method(args):    passmethod = dec_a(dec_b(dec_c(method)))
単一Decorator、パラメータ付き
@dec(params)def method(args):    pass
def method(args):    passmethod = dec(params)(method)
複数のDecorator、パラメータ付き
@dec_a(params1)@dec_b(params2)@dec_c(params3)def method(args):    pass
def method(args):    passmethod = dec_a(params1)(dec_b(params2)(dec_c(params)(method)))
単一のDecorator、パラメータなし
普段服を買いに行くとき、店員さんとどのように話していますか.店員さんはまずあなたに挨拶して、それからあなたの好きな服を試着します.

   
     
def salesgirl(method):
def serve( * args):
print " Salesgirl:Hello, what do you want? " , method. __name__
method(
* args)
return serve

@salesgirl
def try_this_shirt(size):
if size < 35 :
print " I: %d inches is to small to me " % (size)
else :
print " I:%d inches is just enough " % (size)
try_this_shirt(
38 )

 
def salesgirl(method): def serve(*args): print "Salesgirl:Hello, what do you want?", method.__name__ method(*args) return serve @salesgirldef try_this_shirt(size): if size < 35: print "I: %d inches is to small to me"%(size) else: print "I:%d inches is just enough"%(size)try_this_shirt(
結果は次のとおりです.

    
      
Salesgirl:Hello, what do you want? try_this_shirt
I:38 inches is just enough

 
Salesgirl:Hello, what do you want? try_this_shirtI:38 inches is just enough  
これはただの簡単な例で、いくつかの「細部」が処理されていないので、試着してからsalesgirlに買うかどうか教えてあげましょう.のこれでtry_this_shirtメソッドは帯域戻り値に変更する必要があります(boolタイプ、Trueは買いたい、Falseは買いたくない)、salesgirlのserveも戻り値を持ち、戻り値はmethod(*args).
変更後のsalesgirl
 

   
     
def salesgirl(method):
def serve( * args):
print " Salesgirl:Hello, what do you want? " , method. __name__
return method( * args)
return serve

@salesgirl
def try_this_shirt(size):
if size < 35 :
print " I: %d inches is to small to me " % (size)
return False
else :
print " I:%d inches is just enough " % (size)
return True
result
= try_this_shirt( 38 )
print " Mum:do you want to buy this? " , result

結果は次のとおりです.

    
      
Salesgirl:Hello, what do you want? try_this_shirt
I:38 inches is just enough
Mum:do you want to buy this? True

 
Salesgirl:Hello, what do you want? try_this_shirtI:38 inches is just enoughMum:do you want to buy this? True  
 
今私たちのsalesgirlはまだ合格していません.彼女はお客さんに挨拶するだけですが、お客さんが服を買ったら、オファーしません.お客様が買わなければ、他のデザインもお勧めします!
オファーするsalesgirl:

   
     
def salesgirl(method):
def serve( * args):
print " Salesgirl:Hello, what do you want? " , method. __name__
result
= method( * args)
if result:
print " Salesgirl: This shirt is 50$. "
else :
print " Salesgirl: Well, how about trying another style? "
return result
return serve

@salesgirl
def try_this_shirt(size):
if size < 35 :
print " I: %d inches is to small to me " % (size)
return False
else :
print " I:%d inches is just enough " % (size)
return True
result
= try_this_shirt( 38 )
print " Mum:do you want to buy this? " , result

 
def salesgirl(method): def serve(*args): print "Salesgirl:Hello, what do you want?", method.__name__ result = method(*args) if result: print "Salesgirl: This shirt is 50$." else: print "Salesgirl: Well, how about trying another style?" return result return serve @salesgirldef try_this_shirt(size): if size < 35: print "I: %d inches is to small to me"%(size) return False else: print "I:%d inches is just enough"%(size) return Trueresult = try_this_shirt(38)print "Mum:do you want to buy this?", resul
結果は次のとおりです.

   
     
Salesgirl:Hello, what do you want? try_this_shirt
I:38 inches is just enough
Salesgirl: This shirt is 50$.
Mum:do you want to buy this? True

 
Salesgirl:Hello, what do you want? try_this_shirtI:38 inches is just enoughSalesgirl: This shirt is 50$.Mum:do you want to buy this? True
このようなsalesgirlはやっと合格したが、優れているのはまだ遠い.適任のsalesgirlは熟練者に利益を譲るべきだ.古いユーザーは少し利益があるだろう.
 
単一のDecorator、パラメータ付きでオファーされ、割引付きsalesgirl:

   
     
def salesgirl(discount):
def expense(method):
def serve( * args):
print " Salesgirl:Hello, what do you want? " , method. __name__
result
= method( * args)
if result:
print " Salesgirl: This shirt is 50$.As an old user, we promised to discount at %d%% " % (discount)
else :
print " Salesgirl: Well, how about trying another style? "
return result
return serve
return expense

@salesgirl(
50 )
def try_this_shirt(size):
if size < 35 :
print " I: %d inches is to small to me " % (size)
return False
else :
print " I:%d inches is just enough " % (size)
return True
result
= try_this_shirt( 38 )
print " Mum:do you want to buy this? " , result

 
def salesgirl(discount): def expense(method): def serve(*args): print "Salesgirl:Hello, what do you want?", method.__name__ result = method(*args) if result: print "Salesgirl: This shirt is 50$.As an old user, we promised to discount at %d%%"%(discount) else: print "Salesgirl: Well, how about trying another style?" return result return serve return expense @salesgirl(50)def try_this_shirt(size): if size < 35: print "I: %d inches is to small to me"%(size) return False else: print "I:%d inches is just enough"%(size) return Trueresult = try_this_shirt(38)print "Mum:do you want to buy this?", result 
結果は次のとおりです.

   
     
Salesgirl:Hello, what do you want? try_this_shirt
I:38 inches is just enough
Salesgirl: This shirt is 50$.As an old user, we promised to discount at 50%
Mum:do you want to buy this? True

 
Salesgirl:Hello, what do you want? try_this_shirtI:38 inches is just enoughSalesgirl: This shirt is 50$.As an old user, we promised to discount at 50%Mum:do you want to buy this? True  
ここで定義したsalesgirlは、salesgirl記述子がパラメータ付きであり、パラメータが割引であるため、お客様に50%の割引を与えます.もしあなたがこのsalesgirlを初めて見たら、彼女の中にネストされた2つの方法に意外に感じて、大丈夫で、Decoratorに慣れてから、すべてが親切になりました.
続きます
上記も前菜のスープにすぎませんが、次の食事では、Decoratorの高級な使い方をもっと持ってきますので、ご注意ください.