ホームページ  >  記事  >  バックエンド開発  >  Pythonデコレータの詳しい解説

Pythonデコレータの詳しい解説

高洛峰
高洛峰オリジナル
2016-12-01 15:55:172191ブラウズ

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
-executionsexecution結果:

>>> 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 &#39;The args number is too many!&#39;
        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(&#39;The args number is too many!&#39;)
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(&#39;H&#39;, 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(&#39;The args number is too many!&#39;)
        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,&#39;fsaf&#39;)
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(&#39;H&#39;, args)
        except Exception, e:
            raise Exception(e)
        return func(*args, **kwargs)
 
    return ensure_int
 
 
def check_args_num(flag):
    &#39;&#39;&#39;
    :param func: 被装饰函数
    :param flag: 决定是否检查参数数量
    &#39;&#39;&#39;
 
    # 该装饰器用于检查传入的参数数量,检查是否是两个参数
    def get_func(func):
 
        def inner(*args, **kwargs):
            if flag == &#39;false&#39;:
                print &#39;Skip check !&#39;
                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(&#39;The args number is too many!&#39;)
            return func(*args_list, **kwargs)
 
        return inner
 
    return get_func
 
 
@check_args_num(&#39;false&#39;)
@check_args_int
def add(x, y):
    return x + y

上の例からわかるように、クロージャを通じてデコレータを構築する場合、その実行関数の入り口はデコレータ内のネストされた関数です。 , したがって、それが発生する可能性があります。上記の問題の場合、add(1,2,3) が実行されると、内側の関数が最初に実行されます (内側でパラメーターの検証がない場合、ここでは例外はスローされません。リターンの場合のみ例外がスローされます)。 func(*args,* が実行されます) *kwargs)、add(x,y) 関数が実際に呼び出され、この時点で例外がスローされます)。これにより、プログラムが冗長なコードを実行し、メモリと CPU を浪費します。

7. パラメータを使用したデコレータ

デコレータにパラメータを取得させたい場合はどうすればよいでしょうか?

>>>print add(1, 2)
Skip check !
3

このモジュールは比較的単純で、ソース コードを見ることで一目でわかる、言及されていない機能がいくつかあります。原則はすべて Python クロージャを使用して実装されています。 8. functools.wraps( func) デコレータ

functools.wraps とデコレータ モジュールの機能は同じで、どちらもデコレータ関数の署名問題を解決するためのものです。 ここでは、この種のデコレータ構築メソッドのパラメータを使用した例のみを示します。 :

りー


  对比上面通过decorator模块装饰函数的例子,我们可以发现,用decorator装饰函数的代码更加简洁易懂,但是他们二者的执行效率谁更高呢?下面我们通过Timer来测试下:

from timeit import Timer
 
print Timer(&#39;add(1,2)&#39;,setup=&#39;from __main__ import add&#39;).timeit(100000)
 
#将该段代码 加在 之前的例子中
#这里打印的是运行100000次的时间

   

  functools.wraps装饰执行结果:

2.37299322602

   

  decorator模块装饰执行结果:

3.42141566059

   

  执行效率wraps略高,但是这里是执行了10万次他们之间的差距约为1秒,所以我个人还是比较青睐于用decorator模块装饰函数,毕竟看起来易懂,写法也较为简单!本文就将装饰器介绍到这里了,当然也没有说尽装饰器的妙用,比如:装饰类...其原理是用类来当做装饰器,类里面需要用到__call__方法,至于装饰类的用法感兴趣的朋友自行百度咯!

 


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。