Something of the following sort. Imagine this case:
def some_function(a, b):
return a + b
some_magical_workaround({"a": 1, "b": 2, "c": 3}) # returns 3
I can't modify some_function
to add a **kwargs
parameter. How could I create a wrapper function some_magical_workaround
which calls some_function
as shown?
Also, some_magical_workaround
may be used with other functions, and I don't know beforehand what args are defined in the functions being used.
So, you cannot do this in general if the function isn't written in Python (e.g. many built-ins, functions from third-party libraries written as extensions in C) but you can use the inpsect
module to introspect the signature. Here is a quick-and-dirty proof-of-concept, I haven't really considered edge-cases, but this should get you going:
import inspect
def bind_exact_args(func, kwargs):
sig = inspect.signature(func) # will fail with functions not written in Python, e.g. many built-ins
common_keys = sig.parameters.keys() & kwargs.keys()
return func(**{k:kwargs[k] for k in common_keys})
def some_function(a, b):
return a + b
So, a demonstration:
>>> import inspect
>>>
>>> def bind_exact_args(func, kwargs):
... sig = inspect.signature(func) # will fail with functions not written in Python, e.g. many built-ins
... return func(**{k:kwargs[k] for k in sig.parameters.keys() & kwargs.keys()})
...
>>> def some_function(a, b):
... return a + b
...
>>> bind_exact_args(some_function, {"a": 1, "b": 2, "c": 3})
3
But note how it can fail with built-ins:
>>> bind_exact_args(max, {"a": 1, "b": 2, "c": 3})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in bind_exact_args
File "/usr/local/Cellar/python@3.9/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 3113, in signature
return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
File "/usr/local/Cellar/python@3.9/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 2862, in from_callable
return _signature_from_callable(obj, sigcls=cls,
File "/usr/local/Cellar/python@3.9/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 2329, in _signature_from_callable
return _signature_from_builtin(sigcls, obj,
File "/usr/local/Cellar/python@3.9/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 2147, in _signature_from_builtin
raise ValueError("no signature found for builtin {!r}".format(func))
ValueError: no signature found for builtin <built-in function max>
As noted in @JamieDoornbos answer, another example that will not work is a function with positional-only paramters:
E.g.:
def some_function(a, b, /, c):
return a + b + c
Although, you can introspect this:
>>> def some_function(a, b, /, c):
... return a + b + c
...
>>> sig = inspect.signature(some_function)
>>> sig.parameters['a'].kind
<_ParameterKind.POSITIONAL_ONLY: 0>
>>> sig.parameters['b'].kind
<_ParameterKind.POSITIONAL_ONLY: 0>
>>> sig.parameters['c'].kind
<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>
If you need to handle this case, it is certainly possible to, but I leave that as an exercise to the reader :)