Yann F.
Yann F.

Reputation: 23

Dictionary unpacking into function when parameters are incompatible

I'm not sure that it's possible, I would like to unpack dictionary into function but I haven't similar parameters. Is it possible to easily restrict a dictionary to a subset?

def foo(a=0, b=0, c=0):
    print("a=%s, b=%s, c=%s"%(a,b,c))

my_dict = {'a':10, 'b':20, 'd':40}
foo(**my_dict)

Output

TypeError                                 Traceback (most recent call last)
<ipython-input-1-d40731664736> in <module>()
      3 
      4 my_dict = {'a':10, 'b':20, 'd':40}
----> 5 foo(**my_dict)

TypeError: foo() got an unexpected keyword argument 'd'

And I want to obtain

a=10, b=20, c=0

result where 'd' is automatically rejected.

It's just an example. In my case the function is not mine, I cannot redefined her parameters, foo is unmodifiable.

I have many functions in similar case as foo() and these functions are object's methods.

Any ideas?

Upvotes: 2

Views: 103

Answers (2)

Paul Panzer
Paul Panzer

Reputation: 53089

Here is a (hopefully) correct solution using inspect. Since the parameter list returned by inspect may contain star args and also positional only args, it is not enough to just compare the names.

import inspect

def filter_kwds(f, kwds):
    params = inspect.signature(f).parameters
    if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values()):
        return kwds
    else:
        return {k:kwds[k] for k, v in params.items()
                if k in kwds and v.kind in (inspect.Parameter.KEYWORD_ONLY,
                                            inspect.Parameter.POSITIONAL_OR_KEYWORD)}

def f(a, *b, c=4):
    pass

def g(a, *b, c=4, **d):
    pass

print(filter_kwds(f, dict(a=1, b=2, d=4)))
print(filter_kwds(g, dict(a=1, b=2, d=4)))

Output:

{'a': 1}
{'a': 1, 'b': 2, 'd': 4}

Note that the function in the first example omits the name of the star arg, and in the second example detects the presence of a double star arg and therefore does no filtering

Upvotes: 2

deceze
deceze

Reputation: 522500

Inspecting the function signature will help:

import inspect

params = inspect.signature(foo).parameters.keys()

foo(**{k: v for k, v in my_dict.items() if k in params})

Upvotes: 5

Related Questions