ch07. decorator
06 Apr 2019 | pythonDecorator
Decorator의 작동 방식
@decorate
def target():
print('running target()')
은 다음처럼 동작한다.
def target():
print('running target()')
target = decorate(target)
- decorate된 함수가 정의된 직후에 실행됨
변수 범위 규칙
- 함수 scope 내부에 정의되어있지 않은 변수의 경우 global 변수를 이용
- 지역변수를 함수 내부에 만들면 global 변수를 참조하지 않게됨. 따라서 다음과 같은 코드에서 문제가 생김
b = 6
def f2(a):
print(a)
print(b)
b = 9
>>> f2(1)
1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f2
UnboundLocalError: local variable 'b' referenced before assignment
- 이 때는
global b
를 추가해서 b가 전역변수라는 것을 알려주면 됨
Closure
- 함수 본체에서 정의하지 않고 참조하는 비전역(nonlocal) 변수를 포함한 확장 범위를 가진 함수
- free variable을 얻으려면
fn.__code__.co_freevars
를 사용 - 다음처럼 만들면 에러가 남!
def make_averager():
count = 0
total = 0
def averager(new_value):
count += 1
total += new_value
return total / count
return averager
>>> avg = make_averager()
>>> avg(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in averager
UnboundLocalError: local variable 'count' referenced before assignment
count += 1
구문에서count
를 local 변수로 생각했기 때문이다.- 앞의
global
처럼nonlocal count, total
을 붙여주면 해결!
유용한 decorator들
functools.lru_cache()
- decorate할 함수가 받는 인수는 모두 hashable해야함
- dictionary로 결과를 저장하기 때문에…
- 밑의 코드를 돌려보자!
import functools
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
@functools.lru_cache(maxsize=128, typed=False)
def fibonacci_cache(n):
if n < 2:
return n
return fibonacci_cache(n-2) + fibonacci_cache(n-1)
functools.singledispatch()
- method overloading과 비슷함
import functools
@functools.singledispatch
def hello(obj):
raise NotImplemented
@hello.register(int)
def _(i):
print("hello integer! args: {}".format(i))
@hello.register(str)
def _(s):
print("hello string! args: {}".format(s))
매개변수화된 Decorator flask의 route decorator
- flask의
app.route
를 대신해서 보자!
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
@app.route('/')
def index_fn():
return 'hello world'
app.route('/')(index_fn)
=>decorator(index_fn)