Python のデコレータは非常に一般的に使用されるもので、いくつかの特定のメソッドと一般的なメソッドをデコレータとして記述することができるため、コードの可読性と簡潔性、およびスケーラビリティが向上します。 。
Python デコレータを学ぶ前に、次の例を見てみましょう:
1. スコープ
# 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デコレータの中核的な原理です。単純なデコレーターの例:
# coding:utf-8 def add(): msg = 'hello add' def inner(): print msg #运行add()这里会打印'hello add' return inner
>>> obj = add() >>> obj #可见obj是一个指向inner函数的对象 <function inner at 0x0000000002855438> ... >>> obj() hello add #运行obj()打印msg
44。
パラメーターの内容をチェックするための追加のデコレーターがここに追加されます。複数のデコレーターがある場合、最初に Check_args_num が実行され、次に check_args_int が実行されます。
# 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 の例) であることがわかっています。次の例では、デコレータに追加のパラメータを渡す必要もあります。
>>>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' の場合、パラメータ番号のチェックはスキップされます。 出力結果は次のとおりです
# 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 デコレータとその関数の実装も原理は同じです>>> 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>
# 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
>>>print add(1, 2) Skip check ! 3
りー
对比上面通过decorator模块装饰函数的例子,我们可以发现,用decorator装饰函数的代码更加简洁易懂,但是他们二者的执行效率谁更高呢?下面我们通过Timer来测试下:
from timeit import Timer print Timer('add(1,2)',setup='from __main__ import add').timeit(100000) #将该段代码 加在 之前的例子中 #这里打印的是运行100000次的时间
functools.wraps装饰执行结果:
2.37299322602
decorator模块装饰执行结果:
3.42141566059
执行效率wraps略高,但是这里是执行了10万次他们之间的差距约为1秒,所以我个人还是比较青睐于用decorator模块装饰函数,毕竟看起来易懂,写法也较为简单!本文就将装饰器介绍到这里了,当然也没有说尽装饰器的妙用,比如:装饰类...其原理是用类来当做装饰器,类里面需要用到__call__方法,至于装饰类的用法感兴趣的朋友自行百度咯!