티스토리 뷰
이 문서는 파이썬 데코레이터에 대해 간단히 요점정리와 예제를 제공합니다.
<간단 개념 정리>
데코레이터 (=decorator) :
- 다른함수를 인수로 받으면서 callable 을 리턴하는 callable
- 데커레이터는 데커레이트되는 함수(인수로 받은 callable)에 어떤 처리를 수행한뒤, 인수로 받은 함수를 그대로 리턴하거나 다른 콜러블 객체를 리턴한다.
- 런타임이 아닌 import 타임에 실행된다.
# 간단 데코레이터 예제
def decorate(func):
def decorated():
print("데코레이터 실행 전")
func()
print("데코레이터 실행 후")
return decorated
@decorate
def target():
print('target 함수 실행중임')
target()
콜러블 (=callable) :
- 이름 뒤에 괄호를 사용해서 실행(call)할 수 있는 것.
- __call__ 함수를 가지고 있는 클래스의 인스턴스
- 메소드
nonlocal 키워드 :
함수 A 안에 구현된 함수 내장 함수 B의 지역변수가 함수 A 의 변수(자유 변수)를 명시적으로 참조하고자 할때 사용한다.
아래 간단한 예제를 첨부한다.
### 이력을 유지 안하는 잘 못된 고위 함수
def make_averager():
count = 0
total = 0
def averager(value):
count += 1
total += value
return total / count
return averager
avg3 = make_averager()
avg3(10)
### 이력을 유지 안하는 이동평균 계산 (nonlocal 사용)
def make_averager():
count = 0
total = 0
def averager(value):
nonlocal count, total
count += 1
total += value
return total / count
return averager
avg3 = make_averager()
avg3(10)
자유변수(=free variable) :
- 지역범위에 바인딩되어있지 않은 변수
- 글로벌 변수를 특정 함수 안에서 쓰면 해당 글로벌 변수는 자유변수이다.
클로저 (=closure) :
- 함수를 정의할때 존재하던 자유변수에 대한 바인딩을 유지하는 함수
- 클로저는 내포된함수(inner function) 안에서만 의미가 있다
- 클로저는 함수 본체에 정의하지 않고 참조하는 비전역(nonlocal) 변수를 포함한 확장 범위를 가진 함수다.
- 즉 클로져는 inner 함수 + 해당 함수에서 참조하는 자유변수(nonlocal 변수) 까지 포함한 패키지를 뜻한다.
<데코레이터 예제>
### 개선된 clock 데커레이터
import time
import functools
def clock(func):
@functools.wraps(func)
def clocked(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
name = func.__name__
arg_list = []
if args:
arg_list.append(','.join(repr(arg) for arg in args))
if kwargs:
paris = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
arg_list.append(','.join(paris))
arg_str = ','.join(arg_list)
print(f'[{elapsed:.8f}] {name}({arg_str}) -> {result}')
return result
return clocked
@clock
def snooze(seconds):
time.sleep(seconds)
@clock
def factorial(n):
return 1 if n < 2 else n * factorial(n - 1)
print('*' * 40, 'Calling snooze(.123)')
snooze(.123)
print('*' * 40, 'Calling factorial(6)')
print('6! = ', factorial(6))
※ 파이썬에서 제공해주는 functools.wraps 데코레이터 : functools.wraps 를 사용하지 않은 데커레이터는 데커레이트된 함수의 메타데이터를 자신의 것으로 변경한다. functools.wraps 를 사용하면 데커레이트된 함수의 메타데이터를 복사해서 데커레이트의 내포함수에 복사해주게 되므로, 메타 데이터를 덮어 써서 생기는미묘한 문제들을 방지할 수 있다. 그러니 일반적으로 데커레이터를 만들때는 거의 functools.wraps 를 사용해야 한다고 보면 된다
<매개변수(parameter)가 있는 데코레이터 예제>
- parameter 가 있는 데코레이터의 경우, 2 depth 의 내장함수를 가진다.
- 데코레이터 팩토리 -> 데코레이터 -> 데코레이트 되는 함수 순으로 랩핑됨.
# 매개변수화된 clock() 데커레이터
import time
DEFAULT_FMT = '[{elapsed:0.8f}s] {name} ({args}) -> {result}'
def clock(fmt=DEFAULT_FMT):
def decorate(func):
def clocked(*_args):
print('=====================')
t0 = time.time()
_result = func(*_args)
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args)
result = repr(_result)
print(fmt.format(**locals()))
return _result
return clocked
return decorate
if __name__ == '__main__':
@clock()
def snooze(seconds):
time.sleep(seconds)
for i in range(3):
snooze(.123)
=====================
output
=====================
[0.12581611s] snooze (0.123) -> None
=====================
[0.12309694s] snooze (0.123) -> None
=====================
[0.12816191s] snooze (0.123) -> None
@clock('{name} ({args}) dt={elapsed:0.3f}s')
def snooze2(seconds):
time.sleep(seconds)
for i in range(3):
snooze2(.123)
=====================
snooze2 (0.123) dt=0.125s
=====================
snooze2 (0.123) dt=0.126s
=====================
snooze2 (0.123) dt=0.124s
'Python' 카테고리의 다른 글
[파이썬] 리터럴(literal = 숫자&문자)의 종류와 용법 (0) | 2019.04.11 |
---|---|
[Python] 인스턴스 메소드의 종류와 용법 (Instance methods): Public, Protected, Private 접근제어자 (Access Modifiers) (0) | 2019.04.08 |
[파이썬] class method 와 static method (0) | 2019.04.06 |
[Python] Garbage collection (0) | 2019.03.30 |
Django ORM - DB Select 시, String concat 하기. (문자열 합치기) (0) | 2016.11.02 |