>  기사  >  백엔드 개발  >  한 기사로 Python 데코레이터 이해하기

한 기사로 Python 데코레이터 이해하기

WBOY
WBOY앞으로
2023-04-12 21:40:13992검색

한 기사로 Python 데코레이터 이해하기

Python은 초보자에게 친숙한 언어입니다. 그러나 데코레이터와 같이 마스터하기 어려운 고급 기능도 많이 있습니다. 많은 초보자들은 데코레이터와 그 작동 방식을 전혀 이해하지 못했습니다. 이 기사에서는 데코레이터의 모든 것을 소개합니다.

Python에서 함수는 매우 유연한 구조입니다. 변수에 할당하거나, 다른 함수에 매개변수로 전달하거나, 함수의 출력으로 사용할 수 있습니다. 데코레이터는 본질적으로 다른 함수가 수정 없이 일부 기능을 추가할 수 있도록 하는 함수입니다.

이것은 "장식"의 의미입니다. 이 "장식" 자체가 기능을 나타냅니다. 다른 기능을 수정하는 데 사용한다는 것은 해당 기능에 이 기능을 추가하는 것을 의미합니다.

일반적으로 데코레이터에서 제공하는 @ 구문 설탕(Syntactic Sugar)을 사용하여 다른 기능이나 객체를 장식할 수 있습니다. 아래와 같이 @dec 데코레이터를 사용하여 func() 함수를 장식합니다.

@dec
def func():
 pass

데코레이터를 이해하는 가장 좋은 방법은 데코레이터가 해결하는 문제가 무엇인지 이해하는 것입니다. 문제를 해결하고 우아함과 강력함을 보여줍니다.

설정 문제

데코레이터의 목적을 이해하기 위해 간단한 예를 살펴보겠습니다. 두 번째 매개변수의 기본값이 10인 간단한 덧셈 함수 dec.py가 있다고 가정해 보겠습니다.

# dec.py
def add(x, y=10):
 return x + y

이 덧셈 함수를 자세히 살펴보겠습니다.

>>> add(10, 20)
30
>>> add
<function add at 0x7fce0da2fe18>
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>> add.__defaults__ # default value of the `add` function
(10,)
>>> add.__code__.co_varnames # the variable names of the `add` function
('x', 'y')

이것이 무엇인지 이해할 필요는 없습니다. Python의 모든 함수는 객체이며 다양한 속성과 메서드를 가지고 있습니다. 검사 모듈을 통해 add() 함수의 소스 코드를 볼 수도 있습니다:

>>> from inspect import getsource
>>> print(getsource(add))
def add(x, y=10):
 return x + y

이제 어떤 방식으로든 더하기 함수를 사용합니다. 예를 들어 몇 가지 작업을 사용하여 함수를 테스트합니다.

# dec.py
from time import time
def add(x, y=10):
 return x + y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
Output: i
add(10) 20
add(20, 30) 50
add("a", "b") ab

원하는 경우 각 작업의 기능 이해 시간에 대해 시간 모듈을 호출할 수 있습니다:

# dec.py
from time import time
def add(x, y=10):
 return x + y
before = time()
print('add(10)', add(10))
after = time()
print('time taken: ', after - before)
before = time()
print('add(20, 30)', add(20, 30))
after = time()
print('time taken: ', after - before)
before = time()
print('add("a", "b")', add("a", "b"))
after = time()
print('time taken: ', after - before)
Output:
add(10) 20
time taken:6.699562072753906e-05
add(20, 30) 50
time taken:6.9141387939453125e-06
add("a", "b") ab
time taken:6.9141387939453125e-06

이제 프로그래머로서 조금 가려우신가요? 결국 우리는 항상 동일한 코드를 복사하여 붙여넣는 것을 좋아하지 않습니다. 현재 코드는 읽기가 쉽지 않습니다. 무언가를 변경하려면 해당 코드가 나타나는 모든 부분을 수정해야 합니다. Python에는 더 나은 방법이 있을 것입니다.

다음과 같이 추가 기능에서 실행 시간을 직접 캡처할 수 있습니다.

# dec.py
from time import time
def add(x, y=10):
 before = time()
 rv = x + y
 after = time()
 print('time taken: ', after - before)
 return rv
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))

이 방법은 이전 방법보다 확실히 좋습니다. 하지만 다른 기능이 있으면 불편할 것 같습니다. 여러 함수가 있는 경우:

# dec.py
from time import time
def add(x, y=10):
 before = time()
 rv = x + y
 after = time()
 print('time taken: ', after - before)
 return rv
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))

add와 sub가 모두 함수이기 때문에 이를 활용하여 타이머 함수를 작성할 수 있습니다. 우리는 타이머가 함수의 작동 시간을 계산하기를 원합니다:

def timer(func, x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv

이것은 좋지만 다음과 같이 다른 함수를 타이머 함수로 래핑해야 합니다.

print('add(10)', timer(add,10)))

이제 기본값은 여전히 ​​10인가요? 반드시 그런 것은 아닙니다. 그렇다면 어떻게 하면 더 잘할 수 있을까요?

아이디어는 다음과 같습니다. 새 타이머 함수를 만들고, 다른 함수를 래핑하고, 래핑된 함수를 반환합니다.

def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f

이제 add 및 sub 함수를 타이머로 래핑하기만 하면 됩니다.

add = timer(add)

그렇습니다! 전체 코드는 다음과 같습니다.

# dec.py
from time import time
def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
def add(x, y=10):
 return x + y
add = timer(add)
def sub(x, y=10):
 return x - y
sub = timer(sub)
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
Output:
time taken:0.0
add(10) 20
time taken:9.5367431640625e-07
add(20, 30) 50
time taken:0.0
add("a", "b") ab
time taken:9.5367431640625e-07
sub(10) 0
time taken:9.5367431640625e-07
sub(20, 30) -10

프로세스를 요약해 보겠습니다. 추가 기능과 같은 기능이 있고 타이밍과 같은 작업으로 해당 기능을 래핑합니다. 패키징의 결과는 특정한 새로운 기능을 구현할 수 있는 새로운 기능입니다.

물론 기본값에 문제가 있으니 나중에 수정하겠습니다.

Decorator

이제 위의 솔루션은 일반적인 동작을 사용하여 특정 기능을 래핑하는 데코레이터의 아이디어에 매우 가깝습니다. 데코레이터를 사용한 후의 코드는 다음과 같습니다.

def add(x, y=10):
 return x + y
add = timer(add)
You write:
@timer
def add(x, y=10):
 return x + y

둘 다 동일한 효과를 가지며, 이것이 Python 데코레이터가 수행하는 작업입니다. 구현하는 함수는 데코레이터가 함수 위에 구문을 배치하고 구문이 @timer로 더 간단하다는 점을 제외하면 add = 타이머(add)와 유사합니다.

# dec.py
from time import time
def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
@timer
def add(x, y=10):
 return x + y
@timer
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))

매개변수 및 키워드 매개변수

아직 해결되지 않은 작은 문제가 있습니다. 타이머 함수에서는 매개변수 x와 y를 하드코딩합니다. 즉, y의 기본값을 10으로 지정합니다. 인수와 키워드 인수를 함수에 전달하는 방법, 즉 *args 및 **kwargs가 있습니다. 매개변수는 함수의 표준 매개변수(이 경우 x가 매개변수)이고 키워드 매개변수는 이미 기본값(이 경우 y=10)이 있는 매개변수입니다. 코드는 다음과 같습니다.

# dec.py
from time import time
def timer(func):
 def f(*args, **kwargs):
 before = time()
 rv = func(*args, **kwargs)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
@timer
def add(x, y=10):
 return x + y
@timer
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))

이제 타이머 함수는 이러한 매개변수를 함수에 전달하기 때문에 모든 함수, 매개변수 및 기본값 설정을 처리할 수 있습니다.

Higher Order Decorators

궁금하실 수도 있습니다. 유용한 동작을 추가하기 위해 함수를 다른 함수로 래핑할 수 있다면 한 단계 더 나아갈 수 있을까요? 함수를 다른 함수로 래핑하고 다른 함수로 래핑합니까?

예! 실제로 기능은 원하는 만큼 깊어질 수 있습니다. 예를 들어, 함수를 n번 실행하는 데코레이터를 작성하려고 합니다. 아래와 같이:

def ntimes(n):
 def inner(f):
 def wrapper(*args, **kwargs):
 for _ in range(n):
 rv = f(*args, **kwargs)
 return rv
 return wrapper
 return inner

그런 다음 위 함수를 사용하여 이전 기사의 add 함수와 같은 다른 함수를 래핑할 수 있습니다.

@ntimes(3)
def add(x, y):
 print(x + y)
 return x + y

출력 문은 코드가 실제로 3번 실행되었음을 보여줍니다.

위 내용은 한 기사로 Python 데코레이터 이해하기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 51cto.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제