[Effective Python] | January 09, 2021
Effective Python: Second Edition 내용 정리
functools.wraps
python은 함수에 적용할 수 있는 decorators 가 있다.
decorator는 감싸고 있는 함수의 호출 전과 후에 추가적인 코드를 실행할 수 있다.
즉, decorators는 input arguments에 접근하고 수정할 수 있으며, 값을 return하거나 exception을 발생시킬 수 있다.
def trace(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(args, kwargs, result)
return result
return wrapper
@trace
def subtract(n):
"""
Return subtracted number
"""
return n-2
args는 tuple, kwargs는 dict
def trace(func):
def wrapper(n):
result = func(n)
print(n, result)
return result
return wrapper
decorator는 @
를 사용하여 함수에 적용할 수 있는데, @
를 사용하면 function의 decorator를 호출하고 동일한 범위의 original name에 return 값을 할당하는 것과 같다.
a = trace(subtract)
>>> a(6) # trace(subtract)(6)
(6,) {} 4
6 4
4
>>> subtract(6)
(6,) {} 4
4
wrapper
code before and after main function runs.위의 코드들은 잘 작동하지만, 의도되지 않은 side effect가 있다. Decorator의 return 값을 subtract
라고 생각하지 않는다는 것.
print(subtract)
>>> <function trace.<locals>.wrapper at 0x7ff83afc9730>
trace
function은 wrapper
를 return한다.wrapper
function은 decorator
때문에 포함된 모듈 안의 subtract
이름에 할당된 것이다. (The wrapper function is what’s assigned to the main function name in the containing module because of the decorator.)이런 경우 debugging이 어려워진다.
예를 들어 help built-in function를 사용할 수가 없다.
help function은 main 함수의 docstring을 print 해야하는데 다른 결과가 나온다. (subtract 대신 wrapper의 정보)
help(subtract)
>>>
Help on function wrapper in module __main__:
wrapper(n)
또한 pickle과 같은 object serializers에서는 decorated된 original function이 어디에 위치하는지 알 수 없기 때문에 동작하지 않는다.
import pickle
pickle.dumps(subtract)
>>>
AttributeError: Can't pickle local object 'trace.<locals>.wrapper'
functools.wraps
built-in modulewrapper
function에 적용하면, inner function부터 outer function까지의 모든 중요한 metadata를 복사한다.
from functools import wraps
def trace(func):
@wraps(func)
def wrapper(n):
result = func(n)
print(n, result)
return result
return wrapper
@trace
def subtract(n):
"""
Return subtracted number
"""
return n-2
help
function을 적용하면, 예상했던 결과가 잘 나온다.
help(subtract)
>>>
Help on function subtract in module __main__:
subtract(n)
Return subtracted number
Pickle도 잘 동작한다.
pickle.dumps(subtract)
>>> b'\x80\x03c__main__\nsubtract\nq\x00.'
Python functions는 많은 다른 standard attributes가 있다. (__name__
, __module__
, __annotations__
)
이러한 속성들은 function의 interface를 유지하기 위해서 보존되어야 한다.
wraps
를 사용하면 항상 정확한 방식으로 동작하게 할 수 있다.