31

I'm trying to use an OrderedDict, but it keeps being created out of order. For example,

from collections import OrderedDict
OrderedDict(a=1,b=2,c=3)

yields

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

rather than the expected

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

How can I make sure it's created in the proper order I intend?

wogsland
  • 9,106
  • 19
  • 57
  • 93
  • Also see http://stackoverflow.com/questions/8977594/in-python-what-determines-the-order-while-iterating-through-kwargs and http://stackoverflow.com/questions/26748097/using-an-ordereddict-in-kwargs – PM 2Ring Jan 26 '17 at 14:16

5 Answers5

56

collections.OrderedDict keeps track of the order in which elements were added to it. This would work fine in a loop:

c = collections.OrderedDict()
for a,b in zip('abc', (1,2,3)):
    c[a] = b

However, the expression OrderedDict(a=1,b=2,c=3) creates an OrderedDict by passing several keyword arguments to its constructor. In Python 2.7, the order of keyword arguments is not guaranteed. If you want that, you'll have to move to Python 3.6, which implements PEP 468, Preserving the order of **kwargs in a function.

The **kwargs syntax in a function definition indicates that the interpreter should collect all keyword arguments that do not correspond to other named parameters. However, Python does not preserved the order in which those collected keyword arguments were passed to the function. In some contexts the order matters. This PEP dictates that the collected keyword arguments be exposed in the function body as an ordered mapping.

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
  • 2
    Huh. Didn't realize they actually decided to document that behavior and guarantee it. Good to know. – ShadowRanger Jan 26 '17 at 04:39
  • 3
    @ShadowRanger - There's [some ambiguity](https://www.python.org/dev/peps/pep-0468/#performance) about [whether](https://docs.python.org/3.6/whatsnew/3.6.html#new-dict-implementation) the base `dict` type is ordered as anything but a CPython implementation detail, but, yes, keyword arguments are officially part of the language as of 3.6. – TigerhawkT3 Jan 26 '17 at 04:48
  • 2
    @Copperfield - Yes, that sends a single argument to the constructor, which then iterates over that argument in a manner similar to the demonstrated loop. That argument is never sent to an arbitrarily-ordered container, and it's a sequence, so its order is guaranteed. – TigerhawkT3 Jan 26 '17 at 04:51
19

It's weird that it hasn't been mentioned already but the representation of OrderedDict shows you how to create it so that the order is kept:

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

it's not meant as an obstacle that the representation is like that - it's because that representation can be used to create an identical ordered OrderedDict.


Just for completeness (it has been mentioned already), the order is lost because OrderedDict(a=1, b=2, c=3) catches these arguments as **kwargs, which is a normal unordered-dict. At least until python 3.6 came along and made the promise that the order of the kwargs will be kept when you pass it in as you did:

What’s New In Python 3.6

PEP 468: Preserving Keyword Argument Order

**kwargs in a function signature is now guaranteed to be an insertion-order-preserving mapping.

Community
  • 1
  • 1
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • 3
    It's probably worth mentioning that [the following item in that docs link](https://docs.python.org/3.6/whatsnew/3.6.html#new-dict-implementation) says that the plain `dict` in Python 3.6 now maintains insertion order, but currently that should be "considered an implementation detail and should not be relied upon (this may change in the future, but it is desired to have this new dict implementation in the language for a few releases before changing the language spec to mandate order-preserving semantics for all current and future Python implementations" – PM 2Ring Jan 26 '17 at 10:10
  • @PM2Ring Actually the `Preserving Keyword Argument Order` in 3.6 is not an implementation detail. That all **other** `dict`s are ordered in 3.6 is an implementation detail. :-) But it's good to have that comment below the post, it's **important information**. Thanks. – MSeifert Jan 26 '17 at 10:14
  • @Agreed! In my previous comment I wasn't implying that `Preserving Keyword Argument Order` is an implementation detail. But of course, having plain dict maintain insertion order certainly makes preserving keyword arg order a lot easier. :) – PM 2Ring Jan 26 '17 at 10:18
13

Read the docs:

The OrderedDict constructor and update() method both accept keyword arguments, but their order is lost because Python’s function call semantics pass-in keyword arguments using a regular unordered dictionary.

You must pass the input as a sequence of tuples (or an existing ordered dict type) to preserve ordering.

Note that Python 3.6 now provides a guarantee that keyword arguments are passed in the same order they appear in the code (from left to right), thanks to PEP 468, so on Python 3.6 and later, your code will just work.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
3

That is because the keyword arguments (variable = value, ) that you pass will be consolidated into a Python dictionary first. And Python dictionaries are unordered. kwds will be that dictionary as you can see in the Init signature.

Init signature: OrderedDict(self, *args, **kwds)

This is how the OrderedDict will be initialized internally when you pass the keyword arguments:

for key, value in kwds.items():
   self[key] = value

Since kwds is unordered, you will get an unordered OrderedDict.

You may create the ordered dict like so:

from collections import OrderedDict
from string import ascii_lowercase

d = OrderedDict()
for a,b in enumerate(ascii_lowercase[:3], 1):
    d[b] = a

Or:

n=3
d = OrderedDict(zip(ascii_lowercase[:n], range(1,n+1))) 
print d 

Output:

OrderedDict([('a', 1), ('b', 2), ('c', 3)])
Mohammad Yusuf
  • 16,554
  • 10
  • 50
  • 78
1

You can create the desired mapping by using sorted():

dict = {"a":"some_value", "b":"other_value", "c":"foo"}
ordered = OrderedDict(sorted(dict.items(), key=lambda t: t[0]))

This sorts the items prior to passing them to the OrderedDict constructor.

The key= section sets the sorting, and t[0] sorts by the dictionary key.

Andrakis
  • 167
  • 1
  • 4
  • 3
    The key= isunnecessary - simply sorting by the tuple (key, value) will result in a key-sorted dictionary anyway. – xorsyst Jan 26 '17 at 11:28