2

Is there a way in Python to pass explicitly a dictionary to the **kwargs argument of a function? The signature that I'm using is:

def f(*, a=1, **kwargs): pass # same question with def f(a=1, **kwargs)

I tried to call it the following ways:

my_dict=dict(b=2)
f(kwargs=my_dict) # wrong, kwargs receives {'kwargs': {'b': 2}} instead of {'b': 2}
f(**kwargs=my_dict) # SyntaxError: invalid syntax
f(kwargs=**my_dict) # SyntaxError: invalid syntax

The reason I want to do this is that the dictionary I pass in may contain the key a, and I don't want it to pollute the f's argument a:

overlap_dict=dict(a=3,b=4)
f(**overlap_dict) # wrong, 'a' is 3, and 'kwargs' only contains 'b' key
f(a=2, **overlap_dict) # TypeError: f() got multiple values for keyword argument 'a'

Replacing **kwargs with a dictionary in f's signature is not an option for me.

0

3 Answers 3

1

If you don't want to change f to accept more parameters, you could simply make a take a list and the first entry is for f, and the second entry is for kwargs, for instance:

def f(*, a, **kwargs):
    if len(a) == 1:
        a = a[0]
    elif len(a) == 2:
        kwargs['a'] = a[1]
        a = a[0]

You can also do something similar with a dictionary:

def f(*, a, **kwargs):
    if 'kwargs' in a.keys():
        kwargs['a'] = a['kwargs']
    a = a['f']

Alternatively, this does change f's parameter inputs, but I think it's a bit clearer. There are 2 cases I can think of. First, if you know that a will always be set in kwargs:

def f(*, a, a2, **kwargs):
    kwargs['a'] = a2

The other case is if you want it to be optional whether a passes to kwargs:

def f(*, a, a2=None, pass_a=False, **kwargs):
    if pass_a:
        kwargs['a'] = a2
0

Update the kwargs with a and then pass it. Example, say you have a value a=1, then do like this.

kwargs["a"] = a
f(**kwargs)
1
  • Unfotunately, this doesn't seem to fix either of the two usecases I presented (see code after "the reason I want to do this". Please don't hesitate to provide a more described solution for these cases if it does.
    – Codoscope
    Commented Mar 4, 2022 at 11:11
0

You can temporarily (or permanently, if you want – just remove the last line) modify f to take kwargs as a keyword argument. Note that this is a somewhat evil hack, so I would urge you to try other workarounds first.

import inspect


def f(*, a=1, **kwargs):
    print(a)
    print(kwargs)


code = f.__code__
f.__code__ = code.replace(
    co_kwonlyargcount=code.co_kwonlyargcount + 1,
    co_flags=code.co_flags - inspect.CO_VARKEYWORDS,
)
f(kwargs={"a": 3, "b": 4})
f.__code__ = code

This prints:

1
{'a': 3, 'b': 4}
2
  • This is an interesting comment, but it does not solve my issue because it's too hacky in my opinion, and it's still sidestepping my constraint to keep the signature of f (statically and dynamically).
    – Codoscope
    Commented Mar 4, 2022 at 11:54
  • @Codoscope In that case, I believe the answer is: ‘it’s impossible’. Commented Mar 4, 2022 at 12:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.