Why does passing a dictionary as part of *args give us only the keys?

Question:

The setup

Let’s say I have a function:

def variadic(*args, **kwargs):
    print("Positional:", args)
    print("Keyword:", kwargs)

Just for experiment’s sake, I call it with the following:

variadic({'a':5, 'b':'x'}, *{'a':4, 'b':'y'}, **{'a':3, 'b':'z'})

Output:

Positional: ({'a': 5, 'b': 'x'}, 'a', 'b')
Keyword: {'a': 3, 'b': 'z'}

The problem

I don’t have an issue with the keyword arguments.

For the positional ones, however, we get the tuple ({'a': 5, 'b': 'x'}, 'a', 'b'). I understand why we get the first element ({'a': 5, 'b': 'x'}), but I am surprised that we are getting only the keys (a, b) from {'a':4, 'b':'y'}. I don’t understand why this is happening. I think I expected an error from passing a dictionary to *args instead.

Possible answers?

I have not been able to find an answer, but suspect that when parsing *args Python does something along the lines:

for arg in args:
    do something

and since we passed a dictionary, and iterating over a dictionary like this (as opposed to d.items()) gives us only the keys, we just get the keys. Can anyone confirm that or clear up why this is happening?

Asked By: Siyana Pavlova

||

Answers:

In a function call, an argument prefixed with a * must be an iterable value. Each value produced by iterating over the argument is provided to the function as a separate positional argument. (Note that this is independent of a paraemter prefixed with a *, which collects positional arguments not assigned to any other parameter into a tuple.)

Values of type dict are iterable; the iterator for a dict yields the keys of the dict.

Answered By: chepner