Passing default arguments to a decorator in python

2020-04-07 12:39发布

问题:

I am trying to find a way to pass my functions default arguments to the decorator. I have to say I am fairly new to the decorator business, so maybe I just don't understand it properly, but I have not found any answers yet.

So here my modified example from the python functools.wraps manual page.

from functools import wraps
def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
            print 'Calling decorated function'
            print 'args:', args 
            print 'kwargs:', kwds   
            return f(*args, **kwds)
    return wrapper

@my_decorator
def example(i, j=0):
    """Docstring"""
    print 'Called example function'

example(i=1)

I want the j=0 to be passed, too. So that the output should be:

Calling decorated function
args: ()
kwargs: {'i': 1, 'j': 0}
Called example function

回答1:

You can get default argument values by using __defaults__ special attribute.

def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
    print('def args values', f.__defaults__)
    return f(*args, **kwds)
return wrapper

Reference: look for __defaults__ in https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy

A tuple containing default argument values for those arguments that have defaults, or None if no arguments have a default value



回答2:

Default argument is part of function signature. Its not exists in decorator call.

So to use it in wrapper you need to get it out like shown in this question.

import inspect
from functools import wraps

def get_default_args(func):
    signature = inspect.signature(func)
    return {
        k: v.default
        for k, v in signature.parameters.items()
        if v.default is not inspect.Parameter.empty
    }

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
            print('Calling decorated function')
            print('args:', args)
            kwargs = get_default_args(f)
            kwargs.update(kwds)
            print('kwargs:', kwargs)
            return f(*args, **kwds)
    return wrapper

@my_decorator
def example(i, j=0):
    """Docstring"""
    print('Called example function')

example(i=1)

Output:

Calling decorated function
args: ()
kwargs: {'i': 1, 'j': 0}
Called example function