snazzybouche
snazzybouche

Reputation: 2424

How to pop elements from another function's kwargs?

I have a function that is responsible for getting data from the kwargs of several other functions.

The other functions pass their own kwargs to this function along with a keep argument that determines whether or not to keep these properties in the kwargs - i.e. whether to use get or pop.

def _handle_kwargs(keep, **kwargs):
    # keep: whether to keep the kwarg when we're done with it (i.e. get or pop)
    if keep: func = getattr(kwargs, 'get')
    else: func = getattr(kwargs, 'pop')

    # get or pop some kwargs individually
    debug = func('debug', False)
    assert isinstance(debug, bool)
    ...
    # repeated for several different possible kwargs
    return debug, some_other_kwarg, ...

def normal_function(**kwargs)
    debug, some_other_kwarg = _handle_kwargs(False, **kwargs)

Getting the values from the kwargs works fine. However, if I try to pop the kwargs, then they are still present in the original function's kwargs. I suspect this is because _handle_kwargs is only modifying its own kwargs.

How can I ensure that the kwargs are removed if I use pop, even if that's coming from another function?

Upvotes: 1

Views: 794

Answers (2)

Israel Unterman
Israel Unterman

Reputation: 13510

The problem is that you don't pass a dictionary to _handle_kwargs. The **kwargs syntax when calling a function actually "explodes" kwargs.

That is, if kwargs is {'a':1, 'b':2}, then _handle_kwargs(False, **kwargs) is equivalent to _handle_kwargs(False, kwargs['a'], kwargs['b']). You don't pass the kwargs dict at all!

_handle_kwargs collects them into a new dictionary, so it won't affect the original one.

The solution is very simple.

First, def _handle_kwargs(keep, kwargs): without asterisks. Just receive a dict. Second, call it like so:

def normal_function(**kwargs)
    debug, some_other_kwarg = _handle_kwargs(False, kwargs)

See the second line - calling _handle_kwargs without asterisks - just pass the dict.

Upvotes: 1

Deepstop
Deepstop

Reputation: 3817

I doubt you can do that passing to **kwargs, as it appears to be passed by value, but if it's ok to modify the inner function, you could pass kwargs as a plain dictionary, i.e. without the **.

def test(x):
    print(x)
    x.pop('test')
    print(x)

def real(**kwargs):
    test(kwargs)
    print(kwargs)

real(test='nothing', real='something')

Output

{'test': 'nothing', 'real': 'something'}
{'real': 'something'}
{'real': 'something'}

Upvotes: 2

Related Questions