# Fred Baptiste Python Deep Dive
from functools import wraps
Wraps takes a function used in a decorator and adds the functionality of copying over the function name, docstring, arguments list, etc
def memoize(fn):
cache = dict()
@wraps(fn)
def cache_manager(*args):
if args not in cache:
cache[args] = fn(*args)
return cache[args]
return cache_manager
@memoize
def fib(n):
return 1 if n < 3 else fib(n-1) + fib(n-2)
With parameters
from functools import wraps
def count_seq(cntr):
print(f'{cntr}. in cont_seq function')
def decorator(fn):
nonlocal cntr
cntr += 1
print(f'{cntr}. in decorator function')
cntr += 1
@wraps(fn)
def upper_case(*args, **kwargs):
nonlocal cntr
print(f'{cntr}. in upper_case function')
cntr += 1
print(f'{cntr}. Original greet function: {fn(*args)}')
cntr += 1
return f'{cntr}. upper_case function result: {fn(*args).upper()}'
return upper_case
return decorator
@count_seq(1)
def greet(s='hello'):
return s
print(greet('hello world!'))
# Outputs
# 1. in cont_seq function
# 2. in decorator function
# 3. in upper_case function
# 4. Original greet function: hello world!
# 5. upper_case function result: HELLO WORLD!
Another Example
from functools import wraps
def my_decorator(fn):
print('In my_decorator')
@wraps(fn)
def my_wrapper(*args, **kwargs):
kwargs['Whirled'] = 'Peas'
print('In my_wrapper')
print(f'{fn.__name__} was passed to my_wrapper; args: {args} and kwargs: {kwargs}')
print('my_wrapper joins the args and adds a key:value to kwargs')
return fn(' '.join(args), kwargs)
return my_wrapper
@my_decorator
def funk(*args, **kwargs):
"""funk returns *args and **kwargs after some processing by my_wrapper"""
return f'funk returns {args} {kwargs}'
print(funk('Hello', 'World!', Mello='Word', Yellow='Bird'))
print('Function name:', funk.__name__)
print('Function doc string:', funk.__doc__)
# In my_decorator
# In my_wrapper
# funk was passed to my_wrapper; args: ('Hello', 'World!') and kwargs: {'Mello': 'Word', 'Yellow': 'Bird', 'Whirled': 'Peas'}
# my_wrapper joins the args and adds a key:value to kwargs
# funk returns ('Hello World!', {'Mello': 'Word', 'Yellow': 'Bird', 'Whirled': 'Peas'}) {}
# Function name: funk
# Function doc string: funk returns *args and **kwargs after some processing by my_wrapper
Signature
from inspect import signature
from functools import wraps
# The Signature object represents the call signature of a callable object and its return annotation.
def my_decorator1(fn):
def my_wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return my_wrapper
def my_decorator2(fn):
@wraps(fn)
def my_wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return my_wrapper
def funk(a, b, key1='value1', key2='value2'):
pass
@my_decorator1
def foo(a, b, **kwargs):
pass
@my_decorator2
def bar(a, b, key1='value1', key2='value2'):
pass
print('signature without wraps:', signature(my_decorator1(funk)))
print('signature with wraps:', signature(my_decorator2(funk)))
print('signature with wraps:', signature(foo))
print('signature with wraps:', signature(bar))
# signature without wraps: (*args, **kwargs)
# signature with wraps: (a, b, key1='value1', key2='value2')
# signature with wraps: (*args, **kwargs)
# signature with wraps: (a, b, key1='value1', key2='value2')