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
프로세스를 요약해 보겠습니다. 추가 기능과 같은 기능이 있고 타이밍과 같은 작업으로 해당 기능을 래핑합니다. 패키징의 결과는 특정한 새로운 기능을 구현할 수 있는 새로운 기능입니다.
물론 기본값에 문제가 있으니 나중에 수정하겠습니다.
이제 위의 솔루션은 일반적인 동작을 사용하여 특정 기능을 래핑하는 데코레이터의 아이디어에 매우 가깝습니다. 데코레이터를 사용한 후의 코드는 다음과 같습니다.
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))
이제 타이머 함수는 이러한 매개변수를 함수에 전달하기 때문에 모든 함수, 매개변수 및 기본값 설정을 처리할 수 있습니다.
궁금하실 수도 있습니다. 유용한 동작을 추가하기 위해 함수를 다른 함수로 래핑할 수 있다면 한 단계 더 나아갈 수 있을까요? 함수를 다른 함수로 래핑하고 다른 함수로 래핑합니까?
예! 실제로 기능은 원하는 만큼 깊어질 수 있습니다. 예를 들어, 함수를 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!