Give function defaults arguments from a dictionary in Python

Python is designed such that the local variables of any function can be determined unambiguously by looking at the source code of the function. So your proposed syntax

def f(x, **d=d):
  print(x, a, b)

is a nonstarter because there's nothing that indicates whether a and b are local to f or not; it depends on the runtime value of the dictionary, whose value could change across runs.

If you can resign yourself to explicitly listing the names of all of your parameters, you can automatically set their default values at runtime; this has already been well covered in other answers. Listing the parameter names is probably good documentation anyway.

If you really want to synthesize the whole parameter list at run time from the contents of d, you would have to build a string representation of the function definition and pass it to exec. This is how collections.namedtuple works, for example.

Variables in module and class scopes are looked up dynamically, so this is technically valid:

def f(x, **kwargs):
    class C:
        vars().update(kwargs)  # don't do this, please
        print(x, a, b)

But please don't do it except in an IOPCC entry.


How about the **kwargs trick?

def function(arg0, **kwargs):
    print("arg is", arg0, "a is", kwargs["a"], "b is", kwargs["b"])

d = {"a":1, "b":2}
function(0., **d)

outcome:

arg is 0.0 a is 1 b is 2

try this:

# Store the default values in a dictionary
>>> defaults = {
...     'a': 1,
...     'b': 2,
... }
>>> def f(x, **kwa):
        # Each time the function is called, merge the default values and the provided arguments
        # For python >= 3.5:
        args = {**defaults, **kwa}
        # For python < 3.5:
        # Each time the function is called, copy the default values
        args = defaults.copy()
        # Merge the provided arguments into the copied default values
        args.update(kwa)
...     print(args)
... 
>>> f(1, f=2)
{'a': 1, 'b': 2, 'f': 2}
>>> f(1, f=2, b=8)
{'a': 1, 'b': 8, 'f': 2}
>>> f(5, a=3)
{'a': 3, 'b': 2}

Thanks Olvin Roght for pointing out how to nicely merge dictionaries in python >= 3.5


You cannot achieve this at function definition because Python determines the scope of a function statically. Although, it is possible to write a decorator to add in default keyword arguments.

from functools import wraps

def kwargs_decorator(dict_kwargs):
    def wrapper(f):
        @wraps(f)
        def inner_wrapper(*args, **kwargs):
            new_kwargs = {**dict_kwargs, **kwargs}
            return f(*args, **new_kwargs)
        return inner_wrapper
    return wrapper

Usage

@kwargs_decorator({'bar': 1})
def foo(**kwargs):
    print(kwargs['bar'])

foo() # prints 1

Or alternatively if you know the variable names but not their default values...

@kwargs_decorator({'bar': 1})
def foo(bar):
    print(bar)

foo() # prints 1

Caveat

The above can be used, by example, to dynamically generate multiple functions with different default arguments. Although, if the parameters you want to pass are the same for every function, it would be simpler and more idiomatic to simply pass in a dict of parameters.

Tags:

Python