>  기사  >  백엔드 개발  >  Python 데코레이터에 대한 자세한 설명

Python 데코레이터에 대한 자세한 설명

高洛峰
高洛峰원래의
2016-12-01 15:55:172146검색

Python의 데코레이터는 매우 일반적으로 사용되는 것입니다. 몇 가지 특정 메소드와 일반 메소드를 데코레이터로 작성할 수 있습니다. 이는 이러한 메소드를 호출하는 데 큰 편의를 제공하여 코드 가독성, 단순성 및 확장성을 향상시킵니다.

파이썬 데코레이터를 배우기 전에 다음 예제를 살펴보겠습니다.

1.Scope

# 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'

위 예제는 단순히 Python의 범위에 적용됩니다. 다음은 설명입니다. 예제에서는 전역 변수 msg가 선언되어 있고 add 함수에도 지역 변수 msg가 선언되어 있습니다. add()의 "print msg"가 실행되면 add에 지역 변수 msg가 있는지 먼저 찾습니다. 찾을 수 없으면 상위 범위에 변수가 있는지 확인하십시오. 함수가 실행되면 지역 변수도 소멸됩니다. 다음으로 위의 예를 심화해 보겠습니다. 🎜>

2. Closure

# 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

위 예시를 읽어보시고 의문점이 있으시면 obj 내부 함수를 가리키는 객체입니다.실행 시 obj를 사용하면 inner 실행과 동일하지만 add 함수가 실행되지 않습니다. 이는 msg가 선언되지 않고 inner가 msg 변수를 선언하지 않음을 의미합니다. msg 변수의 값을 찾으시겠습니까? 3. 간단한 데코레이터

# 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

실행 결과:

>>>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

4. 다중 데코레이터

# 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

여기에 매개변수 내용 확인을 위한 추가 데코레이터가 추가됩니다. 데코레이터가 여러 개인 경우 Check_args_num이 먼저 실행된 다음 check_args_int가 실행됩니다.

#Here add still이 inner를 가리키는 것을 볼 수 있습니다. 여러 개의 데코레이터가 있을 때 add가 호출되면 호출 항목이 항상 첫 번째 데코레이터가 실행된 후 다음 데코레이터를 실행하고 매개변수를 전달한다는 것을 알 수 있습니다. 시퀀스

>>> 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>

5. 매개변수가 있는 데코레이터

데코레이터를 정의할 때 우리는 알고 있습니다. 데코레이터에 전달된 첫 번째 매개변수는 데코레이팅된 함수입니다(예: 예에 추가) 때로는 추가 매개변수를 데코레이터에 전달해야 합니다. 다음 예에서는 다음과 같이 추가 매개변수를 데코레이터에 전달합니다.

# 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

이 예에서는 check_args_num만 이전과 다릅니다. 차이점은 데코레이터 check_args_num에 추가 매개변수 플래그가 있다는 것입니다. flag=='false'인 경우 매개변수 번호 확인을 건너뛰면 다음과 같은 결과가 나옵니다. 🎜>

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

6. 매개변수가 없는 데코레이터

데코레이터 모듈은 데코레이터를 구체적으로 캡슐화하는 데 사용되는 Python 모듈을 사용하는 것이 더 쉽습니다. 동시에 데코레이팅된 함수의 시그니처는 변경되지 않습니다

. 이전에는 클로저를 통해 Python 데코레이터 구성을 구현하는 방법에 대해 설명했습니다. 여기서는 데코레이터 모듈을 사용하여 장치와 그 원리를 설명합니다. 동일


from decorator import decorator
 
 
@decorator
def check_num(func, *args, **kwargs):
    if len(args) != 2:
        raise Exception(&#39;Your args number is not two&#39;)
    else:
        return func(*args, **kwargs)
 
 
@check_num
def add(x, y):
    return x + y

>>> add
<function add at 0x0000000002D43BA8>
>>> print add(1,2)
3
...
>>> 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>
TypeError: add() takes exactly 2 arguments (3 given)
 
#可以看到这里当我们传三个参数给add()函数时,他直接从add()函数抛出类型错误异常,
#并没有进入check_num装饰器进行参数校验,可见被装饰的函数add()的签名还是他本身
#如果直接构造装饰器,那么这里将会从check_num里面抛出异常,如下:
 
def check_num(func):
    def inner(*args,**kwargs):
        if len(args) != 2:
            raise Exception(&#39;Your args number is not two&#39;)
        else:
            return func(*args,**kwargs)
    return inner
 
@check_num
def add(x, y):
    return x + y
 
 
>>> add
<function inner at 0x0000000002E233C8>
>>>add(1,2,3)
Traceback (most recent call last):
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd.py", line 2411, in <module>
    globals = debugger.run(setup[&#39;file&#39;], None, None, is_module)
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd.py", line 1802, in run
    launch(file, globals, locals)  # execute the script
  File "E:/code/my_project/decorator/test3.py", line 14, in <module>
    print add(1,2,3)
  File "E:/code/my_project/decorator/test3.py", line 4, in inner
    raise Exception(&#39;Your args number is not two&#39;)
Exception: Your args number is not two

위의 예에서 볼 수 있듯이 클로저를 통해 데코레이터를 구성할 때 실행됩니다. 함수 항목은 데코레이터에 중첩된 함수이므로 위와 같은 문제가 발생할 수 있습니다. add(1,2,3)가 실행되면 내부 함수가 먼저 실행됩니다(내부에 매개변수 조정이 없는 경우 원칙적으로, 여기서는 예외가 발생하지 않습니다. 실제로 add(x,y) 함수는 return func(*args,**kwargs)가 실행될 때만 호출됩니다. 그러면 프로그램이 중복 실행됩니다. 코드, 메모리 및 CPU 낭비.

7. 매개변수가 있는 데코레이터

데코레이터가 매개변수를 사용하도록 하려면 어떻게 해야 할까요?

from decorator import decorator
 
 
def check(flag):
    @decorator
    def check_num(func, *args, **kwargs):
        if flag == &#39;false&#39;:
            print &#39;skip check !&#39;
            return func(*args,**kwargs)
        if len(args) != 2:
            raise Exception(&#39;Your args number is not two&#39;)
        else:
            return func(*args, **kwargs)
    return check_num
 
 
@check(&#39;false&#39;)
def add(x, y):
    return x + y
 
>>>add
<function add at 0x0000000002C53C18>
>>>add(1,2)
skip check !
3

데코레이터 모듈에 대한 내용입니다. 이 모듈에는 언급되지 않은 몇 가지 기능이 있습니다. 소스 코드를 보면 원칙은 모두

Python 클로저를 사용하여 구현됩니다. 8. functools.wraps(func) 데코레이터

functools.wraps와 데코레이터 모듈의 기능은 동일합니다. 데코레이팅된 함수의 서명 문제를 해결합니다. 여기에는 이러한 종류의 데코레이터 구성 방법에 대한 매개변수가 포함된 예만 나열되어 있습니다.

import functools
 
def check(flag):
    def wraps(func):
        @functools.wraps(func)
        def check_num(*args, **kwargs):
            if flag == &#39;false&#39;:
                print &#39;skip check !&#39;
                return func(*args,**kwargs)
            if len(args) != 2:
                raise Exception(&#39;Your args number is not two&#39;)
            else:
                return func(*args, **kwargs)
        return check_num
    return wraps
 
@check(&#39;false&#39;)
def add(x, y):
    return x + y

  对比上面通过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으로 문의하세요.