4

How do you iterate over keyword arguments in a Python function? For example, say I have the following method:

class Car():
    def start_engine(self, key, *, driver=None, passenger=None,  gas=None):
        for k,v in <ITERATE_OVER_KEYWORD_ARGUMENT>:
            if v is not None:
                print(f"KEY: {k}    VALUE:{v}")

c = Car()
c.start_engine(key, driver="Bob", passenger="Alice")

Should print:

driver: Bob   
passenger: Alice

I didn't want to use **kwargs because I wanted to name my arguments. I can't just call locals() because I have other arguments.

I guess I could make a helper function that takes in **kwargs and then build a dict, and then I'd call that function with all my keyword arguments, but that seems hacky.


def to_dict(**kwargs):
    d = {k:v for k,v in kwargs.items() if v is not None}
    return d

That works, but seems like such a strange way to accomplish what I'm trying to do.

Is there an acceptable way to to iterate through keyword arguments, or is that bad Python for some reason?

user3240688
  • 1,188
  • 3
  • 13
  • 34
  • [Real Python: Python args and kwargs: Demystified](https://realpython.com/python-kwargs-and-args/) – Trenton McKinney Aug 27 '20 at 21:40
  • Does this answer your question? [Proper way to use \*\*kwargs in Python](https://stackoverflow.com/questions/1098549/proper-way-to-use-kwargs-in-python) – Trenton McKinney Aug 27 '20 at 21:40
  • right. I didn't want to use `**kwargs` because I want to name of variables. – user3240688 Aug 27 '20 at 21:48
  • `kwargs` is already a dictionary, so your don't need to "build" one. Your `to_dict()` does nothing but tear it down (with `**kwargs`), and then puts it back together. – martineau Aug 27 '20 at 21:52
  • user3240688: You could use `**kwargs` in the definition, and easily filter out any unknown/undesired keys in it (`kwards` is a dictionary). – martineau Aug 27 '20 at 21:57
  • You can use the `inspect` module as described here: https://stackoverflow.com/questions/218616/how-to-get-method-parameter-names – Jon Aug 27 '20 at 22:02

1 Answers1

3

In your case they are all optional keyword-only arguments (i.e. arguments with default values) so you can use:

for k in self.start_engine.__kwdefaults__:
    v = locals()[k]

This simple approach will fail to capture keyword-only arguments without default values. The Car shown in your question does not use any, but in case one needs to support those "required keyword" arguments, you may need to inspect signature:

>>> import inspect
>>> class Car:
...     def start_engine(self, key, *, driver=None, passenger=None, dog, gas=None):
...         sig = inspect.signature(self.start_engine)
...         for k, v in sig.parameters.items():
...             if v.kind is inspect.Parameter.KEYWORD_ONLY:
...                 print(k, locals()[k])
...
>>> Car().start_engine("key", gas="unleaded", dog="ridgeback")
driver None
passenger None
dog ridgeback
gas unleaded
wjandrea
  • 28,235
  • 9
  • 60
  • 81
wim
  • 338,267
  • 99
  • 616
  • 750