1. 클로저 관점에서 보기
def perf_clock(func):
def perf_clocked(*args): # func 캡처 → 클로저
...
result = func(*args)
...
return perf_clocked
데코레이터는 사실상 **클로저(Closure)**를 활용합니다.
예제 코드에서 perf_clock 함수는 내부에 perf_clocked라는 함수를 정의하고 반환합니다.
이때 perf_clocked는 외부 함수의 변수 func, 그리고 호출 시 전달되는 *args를 캡처하여 계속 사용할 수 있습니다.
즉, 데코레이터는 클로저 구조 덕분에 원본 함수를 수정하지 않고 기능을 덧붙일 수 있는 것입니다.
2. 실행 시간 측정 예제
아래 코드는 데코레이터 perf_clock을 사용해 함수 실행 시간을 측정하는 예제입니다.
import time
def perf_clock(func):
def perf_clocked(*args):
# 함수 시작 시간
st = time.perf_counter()
result = func(*args)
# 함수 종료 시간 계산
et = time.perf_counter() - st
# 실행 함수명
name = func.__name__
# 함수 매개변수
arg_str = ', '.join(repr(arg) for arg in args)
# 결과 출력
print('[%0.5fs] %s(%s) -> %r' % (et, name, arg_str, result))
return result
return perf_clocked
@perf_clock
def time_func(seconds):
time.sleep(seconds)
@perf_clock
def sum_func(*numbers):
return sum(numbers)
1️⃣ 데코레이터 미사용 (직접 감싸기)
none_deco1 = perf_clock(time_func)
none_deco2 = perf_clock(sum_func)
print(none_deco1, none_deco1.__code__.co_freevars)
print(none_deco2, none_deco2.__code__.co_freevars)
print('-' * 40, 'Called None Decorator -> time_func')
none_deco1(1.5)
print('-' * 40, 'Called None Decorator -> sum_func')
none_deco2(100, 150, 250, 300, 350)
실행 결과 예시
<function perf_clock.<locals>.perf_clocked at 0x...> ('func',)
<function perf_clock.<locals>.perf_clocked at 0x...> ('func',)
---------------------------------------- Called None Decorator -> time_func
[1.50012s] time_func(1.5) -> None
---------------------------------------- Called None Decorator -> sum_func
[0.00001s] sum_func(100, 150, 250, 300, 350) -> 1150
2️⃣ 데코레이터 사용
print('*' * 40, 'Called Decorator -> time_func')
time_func(1.5)
print('*' * 40, 'Called Decorator -> sum_func')
sum_func(100, 150, 250, 300, 350)
실행 결과 예시
**************************************** Called Decorator -> time_func
[1.50010s] time_func(1.5) -> None
**************************************** Called Decorator -> sum_func
[0.00001s] sum_func(100, 150, 250, 300, 350) -> 1150
@perf_clock을 붙이면 원래 함수 코드를 수정하지 않고도 동일한 기능(실행 시간 측정)을 손쉽게 추가할 수 있습니다.
3. 데코레이터의 장점
- 중복 제거 & 코드 간결화
여러 함수에 같은 부가기능(예: 실행 시간 측정, 로깅)을 반복해서 작성할 필요가 없음. - 공통 기능 분리
로깅, 권한 체크, 입력값 검증 같은 기능을 쉽게 모듈화 가능. - 조합 가능
여러 데코레이터를 겹쳐서 붙일 수 있어 기능 확장이 용이함.
4. 데코레이터의 단점
- 가독성 저하 가능성
@데코레이터 문법만 보고는 내부에서 어떤 일이 벌어지는지 한눈에 알기 어렵기 때문에, 처음 접하는 사람에게는 가독성이 떨어질 수 있음. - 특정 기능 전용이라면 불필요
단 한두 개의 함수에서만 쓰는 기능이라면, 굳이 데코레이터로 만들기보다는 그냥 함수 안에서 직접 작성하는 게 직관적임. - 디버깅 불편함
호출되는 함수가 사실상 클로저로 감싸진 다른 함수이기 때문에, 에러 추적 시 원래 함수 이름이나 위치를 바로 파악하기 어려울 수 있음.
정리하면
- 데코레이터는 *클로저(func, args 캡처) 덕분에 가능하다.
- 장점: 중복 제거, 코드 간결, 공통 기능 모듈화, 조합 가능.
- 단점: 가독성이 떨어질 수 있고, 디버깅이 불편하며, 단발성 기능에는 불필요.
즉, 공통 기능을 여러 함수에 일관되게 적용할 때는 강력한 도구, 하지만 단순하고 제한적인 기능이라면 굳이 사용할 필요는 없습니다.
'파이썬 > 기초 프로그래밍' 카테고리의 다른 글
| 파이썬 이터레이터 vs 제너레이터 (0) | 2025.09.08 |
|---|---|
| 파이썬 이터레이터 예제: WordSplitIter (1) | 2025.09.08 |
| 파이썬 클로저와 nonlocal — 언제 필요할까? (0) | 2025.09.08 |
| 파이썬 클로저(Closure)와 nonlocal 키워드 (0) | 2025.09.08 |
| 파이썬 클로저(Closure) 간단 이해 + 내부 구조 들여다보기 (0) | 2025.09.08 |