Preserving signatures of decorated functions
Install decorator module:
$ pip install decorator
Adapt definition of
args_as_ints()
:import decorator@decorator.decoratordef args_as_ints(f, *args, **kwargs): args = [int(x) for x in args] kwargs = dict((k, int(v)) for k, v in kwargs.items()) return f(*args, **kwargs)@args_as_intsdef funny_function(x, y, z=3): """Computes x*y + 2*z""" return x*y + 2*zprint funny_function("3", 4.0, z="5")# 22help(funny_function)# Help on function funny_function in module __main__:# # funny_function(x, y, z=3)# Computes x*y + 2*z
Python 3.4+
functools.wraps()
from stdlib preserves signatures since Python 3.4:
import functoolsdef args_as_ints(func): @functools.wraps(func) def wrapper(*args, **kwargs): args = [int(x) for x in args] kwargs = dict((k, int(v)) for k, v in kwargs.items()) return func(*args, **kwargs) return wrapper@args_as_intsdef funny_function(x, y, z=3): """Computes x*y + 2*z""" return x*y + 2*zprint(funny_function("3", 4.0, z="5"))# 22help(funny_function)# Help on function funny_function in module __main__:## funny_function(x, y, z=3)# Computes x*y + 2*z
functools.wraps()
is available at least since Python 2.5 but it does not preserve the signature there:
help(funny_function)# Help on function funny_function in module __main__:## funny_function(*args, **kwargs)# Computes x*y + 2*z
Notice: *args, **kwargs
instead of x, y, z=3
.
This is solved with Python's standard library functools
and specifically functools.wraps
function, which is designed to "update a wrapper function to look like the wrapped function". It's behaviour depends on Python version, however, as shown below. Applied to the example from the question, the code would look like:
from functools import wrapsdef args_as_ints(f): @wraps(f) def g(*args, **kwargs): args = [int(x) for x in args] kwargs = dict((k, int(v)) for k, v in kwargs.items()) return f(*args, **kwargs) return g@args_as_intsdef funny_function(x, y, z=3): """Computes x*y + 2*z""" return x*y + 2*z
When executed in Python 3, this would produce the following:
>>> funny_function("3", 4.0, z="5")22>>> help(funny_function)Help on function funny_function in module __main__:funny_function(x, y, z=3) Computes x*y + 2*z
Its only drawback is that in Python 2 however, it doesn't update function's argument list. When executed in Python 2, it will produce:
>>> help(funny_function)Help on function funny_function in module __main__:funny_function(*args, **kwargs) Computes x*y + 2*z
There is a decorator module with decorator
decorator you can use:
@decoratordef args_as_ints(f, *args, **kwargs): args = [int(x) for x in args] kwargs = dict((k, int(v)) for k, v in kwargs.items()) return f(*args, **kwargs)
Then the signature and help of the method is preserved:
>>> help(funny_function)Help on function funny_function in module __main__:funny_function(x, y, z=3) Computes x*y + 2*z
EDIT: J. F. Sebastian pointed out that I didn't modify args_as_ints
function -- it is fixed now.