The wrapper function is necessary, and a standard part of how decorator definitions in Python work.
You can, however, help mask the existence of the wrapper function in tracebacks by using functools.wraps()
:
import functools
def f(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
This will update the wrapper function to have the name and docstring of the wrapped function.
--
Decorators are nothing more than functions which are passed a function. This code...
def dec(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@dec
def myfunc(foo, bar):
return foo+bar
is equivalent to this code:
def dec(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def myfunc(foo, bar):
return foo+bar
myfunc = dec(myfunc)
Notice how the thing being passed to dec
is a function which hasn't even been called yet - so there aren't any arguments passed at the time when dec
is invoked. This is why the wrapper function is involved: it adds a layer which will be called when the original function is invoked which can capture arguments.