Python のデコレータは非常に一般的に使用されるもので、いくつかの特定のメソッドと一般的なメソッドをデコレータとして記述することができるため、コードの可読性と簡潔性、およびスケーラビリティが向上します。 。
Python デコレータを学ぶ前に、次の例を見てみましょう:
1. スコープ
1 2 3 4 5 6 7 8 9 10 | # coding:utf-8
msg = 'hello test1'
def add():
msg = 'this is add'
print msg #当执行add()时将打印'this is add'
def add2():
print msg #当执行add2()时将打印'hello test1'
|
ログイン後にコピー
上の例では、グローバル変数 msg とローカル変数 msg を宣言しています。 add() の "print msg" が実行されると、まず add にローカル変数 msg があるかどうかを調べます。見つからない場合は、上位レベルのスコープに移動して変数があるかどうかを調べます。ローカル変数は関数の実行時に生成されますが、関数の終了時にローカル変数も破棄されます。 次に、上記の例を詳しく見てみましょう。上の例で、何か注意点はありますか。obj は inner 関数を指すオブジェクトです。obj を実行する場合、inner を実行するのと同じですが、add 関数は実行されません。つまり、msg は宣言されておらず、inner も実行されません。変数 msg を宣言します。変数 msg はどうやって見つけますか?
これはPythonの「クロージャ」です。Pythonは関数クロージャと呼ばれる機能をサポートしています。人間の言葉で言えば、非グローバルスコープにネストされた関数は、それが定義されたときの場所を記憶できます。これは、関数の obj.func_closure 属性を確認することで結論付けることができます。この属性には、閉じられたスコープ内の値が含まれています (キャプチャされた値のみが含まれます。他の値が add で定義されている場合、閉じられたスコープは次のようになります)。いいえ)閉鎖は、Pythonデコレータの中核的な原理です。単純なデコレーターの例:
1 2 3 4 5 6 7 | # coding:utf-8
def add():
msg = 'hello add'
def inner():
print msg #运行add()这里会打印'hello add'
return inner
|
ログイン後にコピー
-executionsexecution結果:
1 2 3 4 5 6 | >>> obj = add()
>>> obj #可见obj是一个指向inner函数的对象
< function inner at 0x0000000002855438>
...
>>> obj()
hello add #运行obj()打印msg
|
ログイン後にコピー
44。
パラメーターの内容をチェックするための追加のデコレーターがここに追加されます。複数のデコレーターがある場合、最初に Check_args_num が実行され、次に check_args_int が実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # coding:utf-8
def check_args_num(func):
# 该装饰器用于检查传入的参数数量,检查是否是两个参数
def inner(*args, **kwargs):
args_list = list(args)
if len(args_list) < 2:
for i in range(2 - len(args)):
# 如果参数数量小于2则用0填补
args_list.append(0)
if len(args_list) > 2:
# 如果参数数量大于2则打印错误
print 'The args number is too many!'
func(*args_list, **kwargs)
return inner
@check_args_num
def add(x, y):
return x + y
|
ログイン後にコピー
#Here add が inner を指していることがわかります。複数のデコレータが存在する場合、add が呼び出されるとき、最初のデコレータが実行された後、次のデコレータが実行されます。パラメータは順番に渡されます
5. パラメータを持つデコレータ
デコレータを定義するとき、デコレータに渡される最初のパラメータは装飾された関数 (例: add の例) であることがわかっています。次の例では、デコレータに追加のパラメータを渡す必要もあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | >>> print add(1,2)
3
...
>>> print add(100)
100
...
>>> print add(1,2,3)
Traceback (most recent call last):
File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py" , line 3, in Exec
exec exp in global_vars, local_vars
File "<input>" , line 1, in <module>
File "E:/code/my_project/decorator/test1.py" , line 14, in inner
raise Exception('The args number is too many!')
Exception: The args number is too many!
...
>>>add
< function inner at 0x0000000002A6C3C8>
#可以看到add函数现在指向的是inner
|
ログイン後にコピー
この例には、前の例と異なるのは check_args_num だけです。 check_args_num に追加のパラメータフラグがあります。 flag=='false' の場合、パラメータ番号のチェックはスキップされます。 出力結果は次のとおりです
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | # coding:utf-8
def check_args_int(func):
# 该装饰器用于检查传入的参数是否是int型
def ensure_int(*args, **kwargs):
from array import array
try :
array ('H', args)
except Exception, e:
raise Exception(e)
return func(*args, **kwargs)
return ensure_int
def check_args_num(func):
# 该装饰器用于检查传入的参数数量,检查是否是两个参数
def inner(*args, **kwargs):
args_list = list(args)
if len(args_list) < 2:
for i in range(2 - len(args)):
# 如果参数数量小于2则用0填补
args_list.append(0)
if len(args_list) > 2:
# 如果参数数量大于2则打印错误
raise Exception('The args number is too many!')
return func(*args_list, **kwargs)
return inner
@check_args_num
@check_args_int
def add(x, y):
return x + y
|
ログイン後にコピー
6. パラメータのないデコレータ デコレータモジュールは Python A モジュールです。デコレータを具体的にカプセル化するために使用されます。同時に、デコレータを構築するのにデコレータを使用する方が簡単です。 同時に、デコレータ関数のシグネチャは変更されません
ここでは、クロージャを介して Python デコレータ構築を実装することについて説明しました。 Python デコレータとその関数の実装も原理は同じです
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> print add(1,'fsaf')
Traceback (most recent call last):
File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py" , line 3, in Exec
exec exp in global_vars, local_vars
File "<input>" , line 1, in <module>
File "E:/code/my_project/decorator/test1.py" , line 28, in inner
return func(*args_list, **kwargs)
File "E:/code/my_project/decorator/test1.py" , line 10, in ensure_int
raise Exception(e)
Exception: an integer is required
...
>>> add
< function inner at 0x0000000002B1C4A8>
|
ログイン後にコピー
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | # coding:utf-8
def check_args_int(func):
# 该装饰器用于检查传入的参数是否是int型
def ensure_int(*args, **kwargs):
from array import array
try :
array ('H', args)
except Exception, e:
raise Exception(e)
return func(*args, **kwargs)
return ensure_int
def check_args_num(flag):
'''
:param func: 被装饰函数
:param flag: 决定是否检查参数数量
'''
# 该装饰器用于检查传入的参数数量,检查是否是两个参数
def get_func(func):
def inner(*args, **kwargs):
if flag == 'false':
print 'Skip check !'
return func(*args, **kwargs)
args_list = list(args)
if len(args_list) < 2:
for i in range(2 - len(args)):
# 如果参数数量小于2则用0填补
args_list.append(0)
if len(args_list) > 2:
# 如果参数数量大于2则打印错误
raise Exception('The args number is too many!')
return func(*args_list, **kwargs)
return inner
return get_func
@check_args_num('false')
@check_args_int
def add(x, y):
return x + y
|
ログイン後にコピー
上の例からわかるように、クロージャを通じてデコレータを構築する場合、その実行関数の入り口はデコレータ内のネストされた関数です。 , したがって、それが発生する可能性があります。上記の問題の場合、add(1,2,3) が実行されると、内側の関数が最初に実行されます (内側でパラメーターの検証がない場合、ここでは例外はスローされません。リターンの場合のみ例外がスローされます)。 func(*args,* が実行されます) *kwargs)、add(x,y) 関数が実際に呼び出され、この時点で例外がスローされます)。これにより、プログラムが冗長なコードを実行し、メモリと CPU を浪費します。
7. パラメータを使用したデコレータ
デコレータにパラメータを取得させたい場合はどうすればよいでしょうか?
1 2 3 | >>> print add(1, 2)
Skip check !
3
|
ログイン後にコピー
このモジュールは比較的単純で、ソース コードを見ることで一目でわかる、言及されていない機能がいくつかあります。原則はすべて Python クロージャを使用して実装されています。 8. functools.wraps( func) デコレータ
functools.wraps とデコレータ モジュールの機能は同じで、どちらもデコレータ関数の署名問題を解決するためのものです。 ここでは、この種のデコレータ構築メソッドのパラメータを使用した例のみを示します。 :
りー
对比上面通过decorator模块装饰函数的例子,我们可以发现,用decorator装饰函数的代码更加简洁易懂,但是他们二者的执行效率谁更高呢?下面我们通过Timer来测试下:
1 2 3 4 5 6 | from timeit import Timer
print Timer('add(1,2)',setup='from __main__ import add').timeit(100000)
#将该段代码 加在 之前的例子中
#这里打印的是运行100000次的时间
|
ログイン後にコピー
functools.wraps装饰执行结果:
decorator模块装饰执行结果:
执行效率wraps略高,但是这里是执行了10万次他们之间的差距约为1秒,所以我个人还是比较青睐于用decorator模块装饰函数,毕竟看起来易懂,写法也较为简单!本文就将装饰器介绍到这里了,当然也没有说尽装饰器的妙用,比如:装饰类...其原理是用类来当做装饰器,类里面需要用到__call__方法,至于装饰类的用法感兴趣的朋友自行百度咯!