Question

In the below code, how would I obtain *args and **kwargs in function f without the need for the wrapper function?

def f(func):
    def wrapper(*args, **kwargs):
        print(args)
        print(kwargs)
        return func(*args,**kwargs)
    return wrapper

@f
def write(text):
    print(text)

# write = a(write)

write('dog')

Failed attempt 1:

def f(func):
    a=func(*args)
    k=func(**kwargs)

which causes error:

NameError: global name 'args' is not defined

Failed attempt 2:

def f(func(*args,**kwargs)):
    a=func(*args)
    k=func(**kwargs)
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top