Search code examples
pythonfunctionsignaturedefault-argumentsdynamic-typing

Remove all bound arguments from function signature?


Say, we have a function foo, which takes another function bar as an argument and does something based on bar's signature, and, sadly, throws an error when bar has default arguments. I want to use foo with functions, say, bar1,bar2,...bar100, all of which have default arguments. All of the aforementioned functions are from an outer library which I cannot change. There are too many bars to rewrite each of them as a lambda statement. The only way is to remove all bound arguments from these functions. And there's exec, but I'm not that desperate yet.

I know there's a module called inspect in python which works with function signatures, I came up with the following code:

from inspect import signature
from functools import partial

def remove_default_args(func, **kwargs):
    func = partial(func, **kwargs)
    sg = signature(func)
    unbound_parameters = [p for p in sg.parameters.values() if p.default == p.empty]
    func.__signature__ = sg.replace(parameters=unbound_parameters)
    return func

Is there any better way to do this, without inspect? I ask since I know inspect is generally frowned upon, as it only changes the metadata.


Solution

  • Default argument values are stored in the __defaults__ and __kwdefaults__ attributes, which are writable. You can simply set them "back" to None to remove all defaults.

    >>> def foo(x=3, *, y=5):
    ...    pass
    ...
    >>> foo()
    >>> foo.__defaults__, foo.__kwdefaults__
    ((3,), {'y': 5})
    >>> foo.__defaults__ = None
    >>> foo.__kwdefaults__ = None
    >>> foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() missing 1 required positional argument: 'x'