Copy Function Defaults
February 06, 2007

Default arguments to functions are evaluated when the function is created, and are stored in the function object. This cause irritating problems when the default values are in fact mutable, as only single instance exists:

>>> def f(x = []):
...     x.append(5)
...     print x
...
>>> f()
[5]
>>> f()
[5, 5]
>>> f()
[5, 5, 5]

Sometimes it’s the desired behavior, but mostly it’s a bug. To solve that bug, we use

>>> def f(x = None):
...     if x is None:
...         x = []
...     x.append(5)
...     print x
...
>>> f()
[5]
>>> f()
[5]
>>> f()
[5]

But this idiom adds lots of boilerplate code into functions. The following little decorator solves that problem elegantly.

Code

from copy import deepcopy

def copydefaults(func):
    defaults = func.func_defaults
    def wrapper(*args, **kwargs):
        func.func_defaults = deepcopy(defaults)
        return func(*args, **kwargs)
    return wrapper

Example

>>> @copydefaults
... def f(x = []):
...     x.append(5)
...     print x
...
>>>
>>> f()
[5]
>>> f()
[5]
>>> f()
[5]