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_na
buildという方法では、最初の3行はプレイヤーが検証したかどうかを検出するために使用され、非検証のプレイヤーであれば、ログインページにリダイレクトします.検証されたプレイヤーなら、彼が望む建物を建ててあげます.
彼はこのような3行の検証コード(すでに短いが)がいずれかのプレイヤーサービスインタフェースに現れることを指摘した--建物をアップグレードしたり、建物を分解したり、頻繁にチャットしたりして、私はこれがCtrl+C、Ctrl+Vの時間だと言ったが、その時私は深く考えて、もし今需要が変動したら、元の検証に失敗した場合、ログを書く必要があると思っていた.元の3行のコードは4行になります.
もっと苦しいのはif not isが現れるたびにuser_valid(user)の場所にlogを追加warningのこの仕事、これらの时間はあなたが良いお茶を入れて、1首の《Poker Face》を聞いて、tweetは何度も、甚だしきに至っては1つのバグを修復しました......
冒頭のTime Petersが言ったように、いつも最善の方法があります.その後頼総に教えてもらうと、デコルテが祭られます.以下は私がDecoratorに基づいて書き換えた検証コードです.
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
はい、classmethodは関数変換だけをしていますが、fooという名前を2回追加しました.人間は怠惰で進歩したという言葉を覚えています.Decoratorの誕生は、fooを2回少なくした.
読者はすでに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 @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_shirtI:38 inches is just enough
これはただの簡単な例で、いくつかの「細部」が処理されていないので、試着してからsalesgirlに買うかどうか教えてあげましょう.のこれでtry_this_shirtメソッドは帯域戻り値に変更する必要があります(boolタイプ、Trueは買いたい、Falseは買いたくない)、salesgirlのserveも戻り値を持ち、戻り値はmethod(*args).
変更後のsalesgirl
結果は次のとおりです.
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 @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_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 Trueresult = try_this_shirt(38)print "Mum:do you want to buy this?", result
結果は次のとおりです.
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の高級な使い方をもっと持ってきますので、ご注意ください.
いくつかの昔の事
正式に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の高級な使い方をもっと持ってきますので、ご注意ください.