4

This is the following problem:

main_module.py

from collections import OrderedDict
from my_other_module import foo

a = OrderedDict([
    ('a', 1),
    ('b', 2),
    ('c', 3),
    ('d', 4),
    ])

foo(**a)

my_other_module.py

def foo(**kwargs):
    for k, v in kwargs.items():
        print k, v

When i run main_module.py I'm expecting to get printout with the order I specified:

a 1
b 2
c 3
d 4

But instead I'm getting:

a 1
c 3
b 2
d 4

I do understand that this has something to do with the way ** operator is implemented and somehow it looses order how dictionary pairs are passed in. Also I do understand that dictionaries in python are not ordered as lists are, because they're implemented as hash tables. Is there any kind of 'hack' that I could apply so I get the behaviour that is needed in this context?

P.S. - In my situation I can't sort the dictionary inside foo function since there are no rules which could be followed except strict order that values are passed in.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Sir DrinksCoffeeALot
  • 593
  • 2
  • 11
  • 20
  • 4
    Why don't you just pass the `OrderedDict` as an ordinary argument, rather than as `**kwarg`? – Barmar Jun 01 '18 at 08:50

1 Answers1

14

By using **a you're unpacking the ordered dictionary into an argument dictionary.

So when you enter in foo, kwargs is just a plain dictionary, with order not guaranteed (unless you're using Python 3.6+, but that's still an implementation detail in 3.6 - the ordering becomes official in 3.7: Are dictionaries ordered in Python 3.6+?)

You could just lose the packing/unpacking in that case so it's portable for older versions of python.

from collections import OrderedDict

def foo(kwargs):
    for k, v in kwargs.items():
        print(k, v)

a = OrderedDict([
    ('a', 1),
    ('b', 2),
    ('c', 3),
    ('d', 4),
    ])

foo(a)
 
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • Well we wanted to support the case when someone calls `foo` like this: `foo(argument='value')`. By doing what you suggested we will strictly have to pass dictionary type to `foo`. If there aren't any other solutions I guess this is the only valid one. – Sir DrinksCoffeeALot Jun 01 '18 at 09:03
  • in `foo(argument1='value', argument2='value2')`, if you map to `**kwargs` it's the exact same order issue. Unless you're enforcing python 3.6+ where the issue is gone. – Jean-François Fabre Jun 01 '18 at 09:20