>백엔드 개발 >파이썬 튜토리얼 >Python 데코레이터 실행 순서 신화

Python 데코레이터 실행 순서 신화

高洛峰
高洛峰원래의
2017-02-17 11:02:441124검색

여러 데코레이터의 실행 순서 탐색

데코레이터는 Python에서 함수나 코드를 캡슐화하는 데 사용되는 도구입니다. 여기서 제가 논의하고 싶은 내용은 인터넷에서 배울 수 있는 많은 기사입니다. 여러 데코레이터. 시퀀스에 대한 신화.

질문

여러 데코레이터와 관련된 대부분의 함수 호출 시퀀스는 다음 예와 같이 하향식임을 나타냅니다.

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

def decorator_b(func):
    print 'Get in decorator_b'
    def inner_b(*args, **kwargs):
        print 'Get in inner_b'
        return func(*args, **kwargs)
    return inner_b

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

f(1)

위 코드는 먼저 두 가지 함수: decotator_a, decotator_b 이 두 함수의 함수는 함수를 매개변수로 받은 다음 생성된 다른 함수를 반환하는 것입니다(텍스트가 코드보다 더 혼란스럽습니다). 마지막으로 정의된 함수 f는 위에서 정의한 decotator_a, decotator_b를 데코레이션 함수로 사용한다. 1을 매개변수로 하여 데코레이팅된 함수 f를 호출한 후 decotator_a, decotator_b의 순서는 어떻게 됩니까(여기서 함수 실행 순서를 나타내기 위해 print 출력을 사용하여 함수 실행 순서를 확인합니다).

아무 생각 없이 상향식 원칙으로 판단한다면, decorator_a를 먼저 실행한 다음 decorator_b를 실행한 다음 Get in decorator_a, Get in inner_a가 먼저 출력되고 그 다음 Get in decorator_b, Get in inner_b가 출력됩니다. 그러나 이것은 사실이 아니다.

실제 실행 결과는 다음과 같습니다.

Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f

함수와 함수 호출의 차이점

왜 inner_b가 먼저 실행되고 나중에 inner_a가 실행되나요? 위의 문제를 완전히 이해하려면 먼저 함수와 함수 호출이라는 두 가지 개념을 구별해야 합니다. 위의 예에서 f는 함수라고 하고, f(1)은 함수 호출이라고 합니다. 후자는 전자에 의해 전달된 매개변수를 평가한 결과입니다. Python에서는 함수도 객체이므로 f는 함수 객체를 나타내며 그 값은 함수 자체입니다. f(1)은 함수에 대한 호출이고 해당 값은 호출의 결과입니다. 여기서 정의하면 f(1) 값은 2입니다. 마찬가지로 위의 decorator_a 함수를 예로 들면 내부적으로 정의된 함수 개체 inner_a를 반환합니다. inner_a에서는 func 함수가 호출되고 func 호출 결과가 값으로 반환됩니다.

데코레이터 함수는 데코레이팅된 함수가 정의된 직후에 실행됩니다.

두 번째로 명확히 해야 할 질문은 데코레이터가 함수를 데코레이션할 때 정확히 어떤 일이 발생하는지입니다. 이제 다음과 같이 가정하여 예제를 단순화합니다.

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

데코레이터를 소개하는 많은 기사에서 말했듯이:

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

# 相当于
def f(x):
    print 'Get in f'
    return x * 2

f = decorator_a(f)

따라서 인터프리터가 이 코드를 실행할 때 decorator_a는 호출될 때 함수를 사용합니다. f를 매개변수로 사용하고 내부적으로 생성된 함수를 반환하므로 f는 decorator_a에서 반환된 inner_a를 참조합니다. 따라서 향후 f가 호출되면 실제로는 inner_a를 호출하는 것과 같습니다. f에 전달된 매개변수는 inner_a에 전달됩니다. inner_a를 호출하면 수신된 매개변수가 inner_a에 있는 func, 즉 f에 전달됩니다. 최종 반환은 f 값에 의해 호출되는 것이므로 외부에서는 f를 직접 다시 호출하는 것처럼 보입니다.

질문 설명

위의 두 개념이 명확해지면 가장 독창적인 예에서 무슨 일이 일어났는지 명확하게 알 수 있습니다.
인터프리터가 다음 코드를 실행하면 실제로 decorator_a 및 decorator_b를 아래에서 위로 순서대로 호출하고 해당 Get in decorator_a 및 Get in decorator_b가 출력됩니다. 이때 f는 decorator_b의 inner_b와 동일합니다. 하지만 f가 호출되지 않았기 때문에 inner_b가 호출되지 않았고, 비유적으로 inner_b 내부의 inner_a가 호출되지 않았으므로 Get in inner_a와 Get in inner_b가 출력되지 않습니다.

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

그런 다음 마지막 줄에서 매개변수 1로 f를 호출하면 inner_b가 호출됩니다. 먼저 Get in inner_b를 인쇄한 다음 inner_b 내부에서 inner_a를 호출하므로 Get in inner_a가 인쇄됩니다. 원본 f는 inner_a 내부에서 호출되고 그 결과가 최종 결과로 반환됩니다. 이 시점에서 당신은 출력이 왜 그런 것인지 알아야 하고, 데코레이터 실행 시퀀스에서 실제로 무슨 일이 일어나는지 어느 정도 이해해야 합니다.

위 예제의 마지막 줄에서 f 호출을 제거하고 시연을 위해 repl에 넣으면 자연스럽게 순서 문제도 볼 수 있습니다.

➜  test git:(master) ✗ python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test13
Get in decorator_a
Get in decorator_b
>>> test13.f(1)
Get in inner_b
Get in inner_a
Get in f
2
>>> test13.f(2)
Get in inner_b
Get in inner_a
Get in f
4
>>>

실제 적용 시나리오에서는

@login_required
@permision_allowed
def f()
  # Do something
  return

, 위의 방법을 사용하여 두 가지 장식 방법을 작성했습니다. 예를 들어 먼저 @login_required에 로그인했는지 확인한 다음 @permision_allowed 권한이 충분한지 확인합니다.

rrreee

더 많은 Python 장식 장치의 실행 순서에 대한 신화와 관련된 기사는 PHP 중국어 웹 사이트에 주목하십시오!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.