파이썬 함수 데코레이터 만들기 및 연결하기
데코레이터 만들기
데코레이터는 다음과 같이 정의됩니다:
def 데코레이터_함수(함수):
"""데코레이터 역할을 하는 함수입니다."""
# 추가 기능을 구현하는 코드
# 데코레이터가 감싸는 함수를 반환합니다.
return 함수
위 코드에서 데코레이터_함수
는 데코레이터 역할을 하는 함수입니다. 이 함수는 다른 함수를 인수로 받아서 감싸고, 필요에 따라 추가 기능을 구현합니다. 마지막으로, 데코레이터가 감싸는 함수를 반환합니다.
데코레이터 사용하기
@데코레이터_함수
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
# ...
# 함수 호출
함수(인수)
위 코드에서 @데코레이터_함수
는 함수
에 데코레이터를 적용하는 것을 의미합니다. 함수
가 호출될 때, 데코레이터 데코레이터_함수
가 먼저 실행되고, 그 결과 함수
가 실행됩니다.
데코레이터 연결하기
여러 데코레이터를 연결하여 사용할 수 있습니다. 다음과 같이 데코레이터를 여러 번 적용하면 각 데코레이터가 차례대로 실행됩니다:
@데코레이터3
@데코레이터2
@데코레이터1
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
# ...
# 함수 호출
함수(인수)
위 코드에서 함수
는 데코레이터1
, 데코레이터2
, 데코레이터3
순으로 데코레이터가 적용됩니다.
예시
다음은 데코레이터를 사용하여 함수 실행 시간을 측정하는 예시입니다:
import time
def 시간_측정(함수):
"""함수 실행 시간을 측정하는 데코레이터입니다."""
def 래퍼(*args, **kwargs):
시작_시간 = time.time()
결과 = 함수(*args, **kwargs)
종료_시간 = time.time()
print(f"함수 실행 시간: {종료_시간 - 시작_시간:.5f}초")
return 결과
return 래퍼
@시간_측정
def 팩토리얼(n):
"""팩토리얼을 계산하는 함수입니다."""
if n == 0:
return 1
else:
return n * 팩토리얼(n-1)
# 함수 호출
팩토리얼(10)
위 코드를 실행하면 다음과 같은 결과가 출력됩니다:
함수 실행 시간: 0.00002초
예시 코드
로그 기록
def 로그_기록(함수):
"""함수 호출 정보를 로그에 기록하는 데코레이터입니다."""
def 래퍼(*args, **kwargs):
print(f"함수 호출: {함수.__name__}({args}, {kwargs})")
결과 = 함수(*args, **kwargs)
print(f"함수 반환값: {결과}")
return 결과
return 래퍼
@로그_기록
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
return 인수 * 2
# 함수 호출
함수(10)
출력 결과:
함수 호출: 함수(10, {})
함수 반환값: 20
인증 검사
def 인증_검사(함수):
"""사용자 인증을 검사하는 데코레이터입니다."""
def 래퍼(*args, **kwargs):
if not 사용자_인증():
raise ValueError("사용자 인증에 실패했습니다.")
return 함수(*args, **kwargs)
return 래퍼
def 사용자_인증():
"""사용자 인증 여부를 확인하는 함수입니다."""
# ...
@인증_검사
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
return 인수 * 2
# 함수 호출
함수(10)
Traceback (most recent call last):
File "example.py", line 31, in <module>
함수(10)
File "example.py", line 25, in 래퍼
raise ValueError("사용자 인증에 실패했습니다.")
ValueError: 사용자 인증에 실패했습니다.
캐싱
import functools
def 캐싱(함수):
"""함수 결과를 캐싱하는 데코레이터입니다."""
@functools.wraps(함수)
def 래퍼(*args, **kwargs):
키 = (함수.__name__, args, kwargs)
if 키 not in 캐시:
캐시[키] = 함수(*args, **kwargs)
return 캐시[키]
return 래퍼
@캐싱
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
return 인수 * 2
# 함수 호출
함수(10)
함수(10)
20
20
위 코드에서 함수
는 처음 호출될 때 결과를 캐시에 저장합니다. 두 번째 호출에서는 캐시에 저장된 결과를 사용하여 함수 실행 시간을 줄일 수 있습니다.
추가 정보
- 데코레이터는 함수의 기능을 확장하는 데 유용한 도구입니다.
- 다양한 기능을 구현하는 데코레이터를 만들고 사용할 수 있습니다.
- 데코레이터를 연결하여 여러 기능을 함께 사용할 수 있습니다.
데코레이터 대체 방법
직접 함수 수정
단순한 기능을 추가하거나 변경하는 경우, 데코레이터 대신 직접 함수를 수정하는 것이 더 간단할 수 있습니다. 예를 들어, 함수 실행 시간을 측정하는 코드를 함수 내부에 추가할 수 있습니다.
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
시작_시간 = time.time()
결과 = ...
종료_시간 = time.time()
print(f"함수 실행 시간: {종료_시간 - 시작_시간:.5f}초")
return 결과
# 함수 호출
함수(10)
함수 실행 시간: 0.00002초
클래스 사용
데코레이터가 여러 함수에 동일한 기능을 적용하는 경우, 클래스를 사용하여 코드를 더욱 효율적으로 구성할 수 있습니다. 예를 들어, 로그 기록 기능을 제공하는 클래스를 만들 수 있습니다.
class 로그기록:
def __init__(self, 함수):
self.함수 = 함수
def __call__(self, *args, **kwargs):
print(f"함수 호출: {self.함수.__name__}({args}, {kwargs})")
결과 = self.함수(*args, **kwargs)
print(f"함수 반환값: {결과}")
return 결과
@로그기록
def 함수(인수):
"""함수의 본래 기능을 수행합니다."""
return 인수 * 2
# 함수 호출
함수(10)
함수 호출: 함수(10, {})
함수 반환값: 20
믹스인 사용
클래스 상속을 사용하지 않고 기능을 추가할 수 있는 믹스인을 사용할 수 있습니다. 믹스인은 클래스처럼 사용할 수 있지만, 상속 관계를 만들지 않습니다.
class 로그기록Mixin:
def __call__(self, *args, **kwargs):
print(f"함수 호출: {self.__class__.__name__}({args}, {kwargs})")
결과 = super().__call__(*args, **kwargs)
print(f"함수 반환값: {결과}")
return 결과
class 함수(로그기록Mixin):
def __init__(self, 인수):
self.인수 = 인수
def __call__(self):
return self.인수 * 2
# 함수 호출
함수(10)()
함수 호출: 함수(10, {})
함수 반환값: 20
메타클래스 사용
클래스 생성 과정을 조작할 수 있는 메타클래스를 사용하여 데코레이터와 유사한 기능을 구현할 수 있습니다.
class 로그기록메타클래스(type):
def __new__(cls, 이름, 베이스, 속성):
if "__call__" in 속성:
속성["__call__"] = 로그기록Mixin.__call__
return super().__new__(cls, 이름, 베이스, 속성)
class 함수(metaclass=로그기록메타클래스):
def __init__(self, 인수):
self.인수 = 인수
def __call__(self):
return self.인수 * 2
# 함수 호출
함수(10)()
함수 호출: 함수(10, {})
함수 반환값: 20
결론
python function decorator