# Revisit Mutable Default Arguments

**URL:** <https://discuss.python.org/t/revisit-mutable-default-arguments/37525>\
**Category:** Ideas\
**Created:** [October 30, 2023, 3:38pm UTC](https://discuss.python.org/t/revisit-mutable-default-arguments/37525 "2023-10-30T15:38:51Z")\
**Posts on this page:** 1\
**Showing post:** 37

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [October 30, 2023, 7:52pm UTC](https://discuss.python.org/t/revisit-mutable-default-arguments/37525/37 "2023-10-30T19:52:48Z")

</div>

Maybe I’m dancing on my own here but I found this to be a fun exercise. Solving the general case has all kinds of complicated issues, but solving the _specific_ case of “I want my function to default to a fresh mutable object” seems pretty useful and I think it can be solved with `inspect.signature` and `functools.wraps`. This is just a handy utility to replace `if param is None: ...` boilerplate.

```auto
import inspect
from functools import wraps

def mutable_default(default_factory=None, **kwargs):
    """
    Wrap a function such that default None will be replaced with a new object (defaultdict semantics).
    Use kwargs to define multiple factories based on the parameter name. If default_factory=None
    then unspecified parameters with default None will remain as None
    """
    def wrapper(func):
        sig = inspect.signature(func)
        none_args = set(kwargs)
        if default_factory is not None:
            none_args |= {kw for kw, v in sig.parameters.items() if v.default is None}
        
        @wraps(func)
        def wrapped_func(*args, **kwds):
            bound_args = sig.bind(*args, **kwds)
            unbound = none_args.difference(bound_args.arguments)
            bound_args.apply_defaults()
            bound_args.arguments.update(
                (kw, kwargs.get(kw, default_factory)()) for kw in unbound
            )
            return func(*bound_args.args, **bound_args.kwargs)

        return wrapped_func

    return wrapper

```

There’s a little overhead with this, but if the function is doing real work it might be negligible \[1\]. I’ve tested this a bit but I probably haven’t covered every way to call a function.

* * *

1. earlier iterations were faster but covered fewer use cases

---

_[View the full topic](https://discuss.python.org/t/revisit-mutable-default-arguments/37525)._
