12

In python, I wrote this function to teach myself how **kwargs works in Python:

def fxn(a1, **kwargs):
    print a1
    for k in kwargs:
        print k, " : ", kwargs[k]

I then called this function with

fxn(3, a2=2, a3=3, a4=4)

Here was the output that my Python interpreter printed:

3
a3  :  3
a2  :  2
a4  :  4

Why did the for loop print the value of a3 before that of a2 even though I fed a2 into my function first?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
dangerChihuahua007
  • 20,299
  • 35
  • 117
  • 206

6 Answers6

24

kwargs is a dictionary. Dictionaries are unordered - simply put, the order is unspecified and an implementation detail. Peeking under the hood will show that the order varies wildly depending on the hash values of the items, the order of insertion, etc. so you better don't rely on anything related to it.

11

This is a dictionary. And, as mentioned in documentation, dictionary has no order (from http://docs.python.org/tutorial/datastructures.html#dictionaries):

It is best to think of a dictionary as an unordered set of key: value pairs, with the requirement that the keys are unique (within one dictionary).

But you can make it processed in some order, like that:

  • using sorted():

    def fxn(a1, **kwargs):
        print a1
        for k in sorted(kwargs): # notice "kwargs" replaced by "sorted(kwargs)"
            print k, " : ", kwargs[k]
    
  • or by using OrderedDict type (you can pass OrderedDict object as parameter containing all the key-value pairs):

    from collections import OrderedDict
    
    def fxn(a1, ordkwargs):
        print a1
        for k in ordkwargs:
            print k, " : ", ordkwargs[k]
    
    fxn(3, OrderedDict((('a2',2), ('a3',3), ('a4',4))))
    
Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • See my answer with regard to OrderedDict. – PaulMcG Jan 23 '12 at 20:30
  • @PaulMcGuire: See my comment to your answer. – Tadeck Jan 23 '12 at 20:48
  • Wouldn't you write that as `fxn(3, **OrderedDict(etc...`? Oh wait, I see that you changed the signature to take a dict, not a set of keyword arguments. If you are going to change the signature, then you might as well just pass a list of your key-value tuples. But the OP really wanted to iterate over **kwargs. – PaulMcG Jan 23 '12 at 21:11
  • @PaulMcGuire: I explicitly stated, that `kwargs` **has no order**, thus I proposed alternative solutions: 1) iterating through ordered `kwargs`, and 2) passing `OrderedDict` istead of using keyword arguments. Yes, you can pass the tuples, but if you pass tuples, you won't be able to use it like `dict` or `OrderedDict`, would you? :) If you use my second solution, the body of the function does not change much (as it would have to be changed in case of tuples passed in the argument). Compare `dict` vs. `OrderedDict` vs. `tuple` to see what I mean. – Tadeck Jan 23 '12 at 23:31
10

This has finally been introduced in the 3.6 release: dicts are now ordered, therefore the keyword argument order is preserved.

Python 3.6.0 (default, Jan 13 2017, 13:27:48) 
>>> def print_args(**kwargs):
...     print(kwargs.keys())
... 
>>> print_args(first=1, second=2, third=3)
dict_keys(['first', 'second', 'third'])
Todd Sewell
  • 1,444
  • 12
  • 26
afxentios
  • 2,502
  • 2
  • 21
  • 24
5

The unfortunate irony is that the dict-ification of **kwargs means that the following will not work (at least not the way one would expect):

od = OrderedDict(a=1, b=2, c=3)

Since the keyworded args are first built into an unordered dict, you cannot depend that they will be inserted into the OrderedDict in the order they are listed. :(

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
  • 2
    But the following will: `od = OrderedDict((('a',1), ('b',2), ('c',3)))`. So basically `**kwargs`, because it is a `dict`, will not have any order, but you can always pass `OrderedDict` as parameter, and it will have the order you want it to have. – Tadeck Jan 23 '12 at 20:47
2

Since kwargs is a Python dictionary, which is implemented as a hash table, its ordering is not preserved and is effectively random.

Actually, as a fix to a recent security issue in many programming languages, in the future the order may even change between invocations of your program (invocations of the Python interpreter).

drrlvn
  • 8,189
  • 2
  • 43
  • 57
  • 2
    Not random - in fact, it's totally determinate. Just not easily predictable, as it depends on the hash. – Daniel Roseman Jan 23 '12 at 19:53
  • 1
    You're of course right. That is the reason I said "effectively random", to indicate to the reader the he should not attempt to guess it. On a side note, almost everything a computer does is determinate but may not be easily predictable :) – drrlvn Jan 23 '12 at 19:58
1

kwargs is a dictionary, in Python these are not ordered so the result is essentially (pseudo-) random.

Wim
  • 11,091
  • 41
  • 58