Give function defaults arguments from a dictionary in Python

后端 未结 8 2267
予麋鹿
予麋鹿 2021-01-01 18:26

Let\'s imagine I have a dict :

d = {\'a\': 3, \'b\':4}

I want to create a function f that does the exact same thing than this function :

8条回答
  •  清酒与你
    2021-01-01 18:41

    Posting this as an answer because it would be too long for a comment.

    Be careful with this answer. If you try

    @kwargs_decorator(a='a', b='b')
    def f(x, a, b):
        print(f'x = {x}')
        print(f'a = {a}')
        print(f'b = {b}')
    
    f(1, 2)
    

    it will issue an error:

    TypeError: f() got multiple values for argument 'a'
    

    because you are defining a as a positional argument (equal to 2).

    I implemented a workaround, even though I'm not sure if this is the best solution:

    def default_kwargs(**default):
        from functools import wraps
        def decorator(f):
            @wraps(f)
            def wrapper(*args, **kwargs):
                from inspect import getfullargspec
                f_args = getfullargspec(f)[0]
                used_args = f_args[:len(args)]
    
                final_kwargs = {
                    key: value 
                    for key, value in {**default, **kwargs}.items() 
                    if key not in used_args
                }
    
                return f(*args, **final_kwargs)
            return wrapper
        return decorator
    

    In this solution, f_args is a list containing the names of all named positional arguments of f. Then used_args is the list of all parameters that have effectively been passed as positional arguments. Therefore final_kwargs is defined almost exactly like before, except that it checks if the argument (in the case above, a) was already passed as a positional argument.

    For instance, this solution works beautifully with functions such as the following.

    @default_kwargs(a='a', b='b', d='d')
    def f(x, a, b, *args, c='c', d='not d', **kwargs):
        print(f'x = {x}')
        print(f'a = {a}')
        print(f'b = {b}')
        for idx, arg in enumerate(args):
            print(f'arg{idx} = {arg}')
        print(f'c = {c}')
        for key, value in kwargs.items():
            print(f'{key} = {value}')
    
    f(1)
    f(1, 2)
    f(1, b=3)
    f(1, 2, 3, 4)
    f(1, 2, 3, 4, 5, c=6, g=7)
    

    Note also that the default values passed in default_kwargs have higher precedence than the ones defined in f. For example, the default value for d in this case is actually 'd' (defined in default_kwargs), and not 'not d' (defined in f).

提交回复
热议问题