pythonアクセサリーの概要---この1編で十分かもしれません
21262 ワード
Python装飾器(decorator)はプログラム開発でよく使われる機能で、装飾器を合理的に使うことで、私たちのプログラムを虎に翼を添えることができます.
アクセサリー導入
初期及び問題の誕生
もし今1つの会社で、A B Cの3つの業務部門があって、Sの1つの基礎サービス部門があって、現在、S部門は2つの関数を提供して、他の部門に呼び出して、関数は以下の通りです:
初期には、他の部門がこのように呼び出すのは問題なく、会社の業務の発展に伴い、S部門は関数呼び出しに対して権限検証が必要であり、権限があれば呼び出しを行うことができ、そうでなければ呼び出しに失敗する.私たちならどうすればいいか考えてみましょう.
シナリオセット呼び出し元であるABC部門が呼び出すときに、まず権限検証 を自発的に行うようにする. S部門は、対外的に提供する関数のうち、まず権限認証を行い、その後、本格的な関数操作 を行う.
に質問案1は、外層に露出すべきではない権限認証を、使用面に暴露する前に、同時に複数の部門がある場合は、各部門の誰もが知っておく必要があります.他の人が必ずそうする必要はありません.頼りになりません. 案2、行を見ているように見えますが、S部門が対外的により多くの権限検証方法を提供する場合、各関数は権限検証を呼び出す必要があります.同様に苦労して、コードのメンテナンス性と拡張性に不利です.
では、コードのオープンクローズの原則に従って、この問題を完璧に解決する方法はありますか?
アクセサリー導入
答えはあるに違いない.さもないと本当に弱い.まずコードを見て
出力結果は
コードおよび出力から,f 1 f 2関数を呼び出す際に権限検証に成功したことが分かるが,どうすればよいのだろうか.実はここでは装飾器を用いて、閉パッケージ関数w 1を定義することによって、私たちが関数を呼び出す上でキーワード@w 1を通じて、f 1 f 2関数に対して装飾を完成しました.
装飾器の原理
まず、私たちの装飾器関数w 1を見てみましょう.この関数はパラメータfuncを受信します.実はメソッド名を受信します.w 1の内部には関数innerを定義し、inner関数に権限チェックを追加し、権限を検証した後に伝達されたパラメータfuncを呼び出します.同時に、w 1の戻り値は内部関数innerで、実は閉包関数です.
それから、もう一度見て、f 1に@w 1を追加します.それはどういう意味ですか.python解釈器がこの文を実行すると、w 1関数が呼び出され、同時に装飾された関数名がパラメータとして伝達する(この場合f 1)、閉パケット一文分析によれば、w 1関数を実行する際、直接inner関数が戻るとともにf 1に付与され、このときのf 1は装飾されていない時のf 1ではなく、w 1を指す.inner関数アドレス.
次に、f 1()を呼び出すとき、実はw 1を呼び出す.Inner関数では、このとき権限検証を実行してから元のf 1()を呼び出し、そこのf 1は装飾によって渡されたパラメータf 1である.
このようにしてf 1の装飾が完了し,権限検証が実現される.
アクセサリーの知識点
実行タイミング
デコレーションの原理を知って、その実行タイミングはどうなっているのか、次に見てみましょう.国際慣例、先にコードをつける
出力結果は
これにより、python解釈器が@w 1に実行されると、以下のコードが実行されることに相当する装飾が開始されることが分かった.
2つの装飾品実行プロセスと装飾結果
2つ以上の装飾器が1つの関数を装飾している場合、実行フローと装飾結果はどのようなものですか?同様に、問題をコードで説明します.
出力結果:
まず第2のアクセラレータ(makeItalic)で装飾し、次に第1のアクセラレータ(makeBold)で装飾し、呼び出し中に第1のアクセラレータ(makeBold)を先に実行し、次に第2のアクセラレータ(makeItalic)を実行することが分かる.
なぜか、2つのステップに分けて分析してみましょう. 装飾タイミング上記装飾タイミングの紹介により、@makeBoldまで実行する場合、以下の関数を装飾する必要があることがわかります.このとき解釈器は下に進み続け、関数名ではなく装飾器であることがわかります.この場合、@makeBold装飾器は実行を一時停止し、次の装飾器@makeItalicを実行します.次にtest関数名をデザイナ関数に入力して’b’を印刷し、makeItalicデザイナが完了すると、そのときのtestがmakeItalicのinner関数アドレスを指し、そのときに戻って@makeBoldを実行し、次に新しいtestをmakeBoldデザイナ関数に入力するので‘a’を印刷した. test関数を呼び出すとき、上記の解析による、このときtestはmakeBoldを指す.inner関数なので、まず‘1’,次にfun()を呼び出すときは、実は呼び出されたmakeItalic.inner()関数なので「2」を印刷しmakeItalic.Innerでは、呼び出されたfunこそが私たちの最もオリジナルのtest関数なので、オリジナルのtest関数の「c',‘3'」を印刷するので、1層ずつ調整した後、印刷の結果は
無参関数の装飾
上記の例のf 1 f 2はいずれも無パラメトリック関数の装飾であり、単独では例を挙げない
パラメトリック関数の装飾
使用中にパラメータが付いている関数もあるかもしれませんが、これはどのように処理しますか?コード優先:
出力結果:
具体的な説明コードの注釈はすでにあり、単独で説明しません.このとき、それはパラメータで、複数または不定長パラメータであれば、どのように処理すればいいのかと聞かれるかもしれません.次のコードを見てください.秒で分かります.
出力結果:
pythonの可変パラメータを用いて,装飾テープパラメータの関数を容易に実現した.
戻り値付き関数の装飾
次に、戻り値のある関数を飾ります.前の書き方では、コードはこうです.
出力結果:
この場合、test関数の「hello」が出力されていないのではなく、Noneであることがわかります.それはなぜでしょうか.inner関数でtestを呼び出したのですが、戻り値を受け入れられず、戻りも行われていません.デフォルトはNoneです.原因がわかりました.では、コードを修正します.
出力結果:
これにより,戻り値パラメータ付き関数の装飾が完了すると予想される.
パラメータ付き装飾器
パラメータ付き関数と戻り値のある関数を装飾することを紹介しましたが、パラメータ付き装飾器はありますか?もしあれば、何の役に立ちますか?答えはあるに違いないので、次はコードで見てみましょう.
出力結果:
簡単に理解すると、パラメータ付きの装飾器は元の閉包に加えて閉包し、外層関数func_を通じてargsの戻り値w_test_logは見て、具体的な実行プロセスは注釈の中ですでに説明しました.利点は,実行時に異なるパラメータに対して異なる応用機能処理を行うことである.
ユニバーサルデコレーション
こんなにたくさん紹介しましたが、実際の応用では、カテゴリのない関数に対して装飾器を1つ書くと、疲れてしまうと思います.では、汎用万能装飾器があるかどうか、答えはあるに違いありません.くだらないことは言わないで、直接コードをつけます.
出力結果:
上記のいくつかの例を組み合わせると、汎用装飾器の機能が完成し、原理は同じで、くだらない話にすぎない.
クラス装飾器
アクセラレータ関数は、パラメータとしてcallableオブジェクトを受け入れ、callableオブジェクトを返すインタフェースコンストレイントです.pythonでは、一般的にcallableオブジェクトは関数ですが、例外もあります.たとえば、あるオブジェクトがcallメソッドを書き換えると、このオブジェクトはcallableになります.
オブジェクトを作成した後、このオブジェクトを直接実行すると、callableではなく、直接実行できないため、例外が放出されますが、変更すると、呼び出しを直接実行できます.次のようになります.
出力:
次に、本題を導入して、クラス装飾関数の使い方を見てみましょう.
出力結果:
従来の原理と同様にpythonインタプリタが@Testに実行されると、現在のtest関数がパラメータとしてTestオブジェクトに渡され、initメソッドが呼び出され、同時にtest関数が作成されたTestオブジェクトに指し示すと、次にtest()が実行されると、実際には作成されたオブジェクトを直接呼び出し、そのcallメソッドが実行される.
はい、今までpythonアクセサリーと関連知識点を基本的に話しましたが、問題があれば、指摘してください.
アクセサリー導入
初期及び問題の誕生
もし今1つの会社で、A B Cの3つの業務部門があって、Sの1つの基礎サービス部門があって、現在、S部門は2つの関数を提供して、他の部門に呼び出して、関数は以下の通りです:
def f1():
print('f1 called')
def f2():
print('f2 called')
初期には、他の部門がこのように呼び出すのは問題なく、会社の業務の発展に伴い、S部門は関数呼び出しに対して権限検証が必要であり、権限があれば呼び出しを行うことができ、そうでなければ呼び出しに失敗する.私たちならどうすればいいか考えてみましょう.
シナリオセット
に質問
では、コードのオープンクローズの原則に従って、この問題を完璧に解決する方法はありますか?
アクセサリー導入
答えはあるに違いない.さもないと本当に弱い.まずコードを見て
def w1(func):
def inner():
print('... ...')
func()
return inner
@w1
def f1():
print('f1 called')
@w1
def f2():
print('f2 called')
f1()
f2()
出力結果は
... ...
f1 called
... ...
f2 called
コードおよび出力から,f 1 f 2関数を呼び出す際に権限検証に成功したことが分かるが,どうすればよいのだろうか.実はここでは装飾器を用いて、閉パッケージ関数w 1を定義することによって、私たちが関数を呼び出す上でキーワード@w 1を通じて、f 1 f 2関数に対して装飾を完成しました.
装飾器の原理
まず、私たちの装飾器関数w 1を見てみましょう.この関数はパラメータfuncを受信します.実はメソッド名を受信します.w 1の内部には関数innerを定義し、inner関数に権限チェックを追加し、権限を検証した後に伝達されたパラメータfuncを呼び出します.同時に、w 1の戻り値は内部関数innerで、実は閉包関数です.
それから、もう一度見て、f 1に@w 1を追加します.それはどういう意味ですか.python解釈器がこの文を実行すると、w 1関数が呼び出され、同時に装飾された関数名がパラメータとして伝達する(この場合f 1)、閉パケット一文分析によれば、w 1関数を実行する際、直接inner関数が戻るとともにf 1に付与され、このときのf 1は装飾されていない時のf 1ではなく、w 1を指す.inner関数アドレス.
次に、f 1()を呼び出すとき、実はw 1を呼び出す.Inner関数では、このとき権限検証を実行してから元のf 1()を呼び出し、そこのf 1は装飾によって渡されたパラメータf 1である.
このようにしてf 1の装飾が完了し,権限検証が実現される.
アクセサリーの知識点
実行タイミング
デコレーションの原理を知って、その実行タイミングはどうなっているのか、次に見てみましょう.国際慣例、先にコードをつける
def w1(fun):
print('... ...')
def inner():
print('... ...')
fun()
return inner
@w1
def test():
print('test')
test()
出力結果は
... ...
... ...
test
これにより、python解釈器が@w 1に実行されると、以下のコードが実行されることに相当する装飾が開始されることが分かった.
test = w1(test)
2つの装飾品実行プロセスと装飾結果
2つ以上の装飾器が1つの関数を装飾している場合、実行フローと装飾結果はどのようなものですか?同様に、問題をコードで説明します.
def makeBold(fun):
print('----a----')
def inner():
print('----1----')
return '' + fun() + ''
return inner
def makeItalic(fun):
print('----b----')
def inner():
print('----2----')
return '' + fun() + ''
return inner
@makeBold
@makeItalic
def test():
print('----c----')
print('----3----')
return 'hello python decorator'
ret = test()
print(ret)
出力結果:
----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>
まず第2のアクセラレータ(makeItalic)で装飾し、次に第1のアクセラレータ(makeBold)で装飾し、呼び出し中に第1のアクセラレータ(makeBold)を先に実行し、次に第2のアクセラレータ(makeItalic)を実行することが分かる.
なぜか、2つのステップに分けて分析してみましょう.
hello python decorator
です.無参関数の装飾
上記の例のf 1 f 2はいずれも無パラメトリック関数の装飾であり、単独では例を挙げない
パラメトリック関数の装飾
使用中にパラメータが付いている関数もあるかもしれませんが、これはどのように処理しますか?コード優先:
def w_say(fun):
"""
, ,
"""
def inner(name):
"""
,
:param name:
:return:
"""
print('say inner called')
fun(name)
return inner
@w_say
def hello(name):
print('hello ' + name)
hello('wangcai')
出力結果:
say inner called
hello wangcai
具体的な説明コードの注釈はすでにあり、単独で説明しません.このとき、それはパラメータで、複数または不定長パラメータであれば、どのように処理すればいいのかと聞かれるかもしれません.次のコードを見てください.秒で分かります.
def w_add(func):
def inner(*args, **kwargs):
print('add inner called')
func(*args, **kwargs)
return inner
@w_add
def add(a, b):
print('%d + %d = %d' % (a, b, a + b))
@w_add
def add2(a, b, c):
print('%d + %d + %d = %d' % (a, b, c, a + b + c))
add(2, 4)
add2(2, 4, 6)
出力結果:
add inner called
2 + 4 = 6
add inner called
2 + 4 + 6 = 12
pythonの可変パラメータを用いて,装飾テープパラメータの関数を容易に実現した.
戻り値付き関数の装飾
次に、戻り値のある関数を飾ります.前の書き方では、コードはこうです.
def w_test(func):
def inner():
print('w_test inner called start')
func()
print('w_test inner called end')
return inner
@w_test
def test():
print('this is test fun')
return 'hello'
ret = test()
print('ret value is %s' % ret)
出力結果:
w_test inner called start
this is test fun
w_test inner called end
ret value is None
この場合、test関数の「hello」が出力されていないのではなく、Noneであることがわかります.それはなぜでしょうか.inner関数でtestを呼び出したのですが、戻り値を受け入れられず、戻りも行われていません.デフォルトはNoneです.原因がわかりました.では、コードを修正します.
def w_test(func):
def inner():
print('w_test inner called start')
str = func()
print('w_test inner called end')
return str
return inner
@w_test
def test():
print('this is test fun')
return 'hello'
ret = test()
print('ret value is %s' % ret)
出力結果:
w_test inner called start
this is test fun
w_test inner called end
ret value is hello
これにより,戻り値パラメータ付き関数の装飾が完了すると予想される.
パラメータ付き装飾器
パラメータ付き関数と戻り値のある関数を装飾することを紹介しましたが、パラメータ付き装飾器はありますか?もしあれば、何の役に立ちますか?答えはあるに違いないので、次はコードで見てみましょう.
def func_args(pre='xiaoqiang'):
def w_test_log(func):
def inner():
print('... ...visitor is %s' % pre)
func()
return inner
return w_test_log
# ,
# func_args('wangcai'), w_test_log
# @w_test_log
# @w_test_log test_log
@func_args('wangcai')
def test_log():
print('this is test log')
test_log()
出力結果:
... ...visitor is wangcai
this is test log
簡単に理解すると、パラメータ付きの装飾器は元の閉包に加えて閉包し、外層関数func_を通じてargsの戻り値w_test_logは見て、具体的な実行プロセスは注釈の中ですでに説明しました.利点は,実行時に異なるパラメータに対して異なる応用機能処理を行うことである.
ユニバーサルデコレーション
こんなにたくさん紹介しましたが、実際の応用では、カテゴリのない関数に対して装飾器を1つ書くと、疲れてしまうと思います.では、汎用万能装飾器があるかどうか、答えはあるに違いありません.くだらないことは言わないで、直接コードをつけます.
def w_test(func):
def inner(*args, **kwargs):
ret = func(*args, **kwargs)
return ret
return inner
@w_test
def test():
print('test called')
@w_test
def test1():
print('test1 called')
return 'python'
@w_test
def test2(a):
print('test2 called and value is %d ' % a)
test()
test1()
test2(9)
出力結果:
test called
test1 called
test2 called and value is 9
上記のいくつかの例を組み合わせると、汎用装飾器の機能が完成し、原理は同じで、くだらない話にすぎない.
クラス装飾器
アクセラレータ関数は、パラメータとしてcallableオブジェクトを受け入れ、callableオブジェクトを返すインタフェースコンストレイントです.pythonでは、一般的にcallableオブジェクトは関数ですが、例外もあります.たとえば、あるオブジェクトがcallメソッドを書き換えると、このオブジェクトはcallableになります.
オブジェクトを作成した後、このオブジェクトを直接実行すると、callableではなく、直接実行できないため、例外が放出されますが、変更すると、呼び出しを直接実行できます.次のようになります.
class Test(object):
def __call__(self, *args, **kwargs):
print('call called')
t = Test()
print(t())
出力:
call called
次に、本題を導入して、クラス装飾関数の使い方を見てみましょう.
class Test(object):
def __init__(self, func):
print('test init')
print('func name is %s ' % func.__name__)
self.__func = func
def __call__(self, *args, **kwargs):
print(' ')
self.__func()
@Test
def test():
print('this is test func')
test()
出力結果:
test init
func name is test
this is test func
従来の原理と同様にpythonインタプリタが@Testに実行されると、現在のtest関数がパラメータとしてTestオブジェクトに渡され、initメソッドが呼び出され、同時にtest関数が作成されたTestオブジェクトに指し示すと、次にtest()が実行されると、実際には作成されたオブジェクトを直接呼び出し、そのcallメソッドが実行される.
はい、今までpythonアクセサリーと関連知識点を基本的に話しましたが、問題があれば、指摘してください.