Reputation: 14970
I am trying to get a sorted dictionary. But the order of the items between mydict
and orddict
doesn't seem to change.
from collections import OrderedDict
mydict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
orddict = OrderedDict(mydict)
print(mydict, orddict)
# print items in mydict:
print('mydict')
for k, v in mydict.items():
print(k, v)
print('ordereddict')
# print items in ordered dictionary
for k, v in orddict.items():
print(k, v)
# print the dictionary keys
# for key in mydict.keys():
# print(key)
# print the dictionary values
# for value in mydict.values():
# print(value)
Upvotes: 39
Views: 28130
Reputation: 43642
As of Python 3.7, a new improvement to the dict
built-in is:
the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.
This means there is no real need for OrderedDict
anymore 🎉. They are almost the same.
Here are some comparisons between Python 3.7+ dict
and OrderedDict
:
from collections import OrderedDict
d = {'b': 1, 'a': 2}
od = OrderedDict([('b', 1), ('a', 2)])
# they are equal with content and order
assert d == od
assert list(d.items()) == list(od.items())
assert repr(dict(od)) == repr(d)
Obviously, there is a difference between the string representation of the two object, with the dict
object in more natural and compact form.
str(d) # {'b': 1, 'a': 2}
str(od) # OrderedDict([('b', 1), ('a', 2)])
As for different methods between the two, this question can be answered with set theory:
d_set = set(dir(d))
od_set = set(dir(od))
od_set.difference(d_set)
# {'__dict__', '__reversed__', 'move_to_end'} for Python 3.7
# {'__dict__', 'move_to_end'} for Python 3.8+
This means OrderedDict
has at most two features that dict
does not have built-in, but work-arounds are shown here:
__reversed__
/ reversed()
No workaround is really needed for Python 3.8+, which fixed this issue. OrderedDict
can be "reversed", which simply reverses the keys (not the whole dictionary):
reversed(od) # <odict_iterator at 0x7fc03f119888>
list(reversed(od)) # ['a', 'b']
# with Python 3.7:
reversed(d) # TypeError: 'dict' object is not reversible
list(reversed(list(d.keys()))) # ['a', 'b']
# with Python 3.8+:
reversed(d) # <dict_reversekeyiterator at 0x16caf9d2a90>
list(reversed(d)) # ['a', 'b']
To properly reverse a whole dictionary using Python 3.7+:
dict(reversed(list(d.items()))) # {'a': 2, 'b': 1}
move_to_end
OrderedDict
has a move_to_end
method, which is simple to implement:
od.move_to_end('b') # now it is: OrderedDict([('a', 2), ('b', 1)])
d['b'] = d.pop('b') # now it is: {'a': 2, 'b': 1}
popitem
OrderedDict
and dict
both have a similar popitem
method that work the same for popping the last item:
p1 = od.popitem(last=True) # now it is: OrderedDict([('b', 1)])
p2 = d.popitem() # now it is: {'b': 1}
p1 == p2 # same values: {'a': 2}
How to pop the first item, since the popitem
for dict
doesn't take arguments:
p1 = od.popitem(last=False) # now it is: OrderedDict([('a', 2)])
p2 = d.pop(next(iter(d))) # now it is: {'a': 2}
p1 == p2 # same values: {'b': 1}
These approaches avoid iterating through the whole list to remove items, making them as fast in both cases as popitem
in OrderedDict.
Upvotes: 79
Reputation:
Starting with CPython 3.6 and all other Python implementations starting with Python 3.7, the built-in dict
is ordered - you get the items out in the order you inserted them. Which makes dict
and OrderedDict
effectively the same.
The documentation for OrderedDict
lists the remaining differences. The most important one is that
OrderedDict
checks for matching order.Then there's a few minor practical differences:
dict.popitem()
takes no arguments, whereas OrderedDict.popitem(last=True)
accepts an optional last=
argument that lets you pop the first item instead of the last item.OrderedDict
has a move_to_end(key, last=True)
method to efficiently reposition an element to the end or the beginning. With dict
s you can move a key to the end by re-inserting it: mydict['key'] = mydict.pop('key')
reversed(OrderedDict())
but reversed({})
would raise a TypeError: 'dict' object is not reversible
error because they forgot to add a __reversed__
dunder method to dict
when they made it ordered. This is now fixed.And there are a few under-the-hood differences that might mean that you could get better performance for some specific usecase with OrderedDict
:
dict
was designed to be very good at mapping
operations. Tracking insertion order was secondary.OrderedDict
was designed to be good at reordering operations.
Space efficiency, iteration speed, and the performance of update
operations were secondary.OrderedDict
can handle frequent reordering
operations better than dict
. This makes it suitable for tracking
recent accesses (for example in an LRU cache).See this great talk from 2016 by Raymond Hettinger for details on how Python dictionaries are implemented.
Upvotes: 9
Reputation: 2838
Adding on to the answer by Brian, OrderedDict
is really great. Here's why:
You can use it as simple dict
object because it supports equality testing with other Mapping
objects like collections.counter.
OrderedDict
preserves the insertion order as explained by Brian. In addition to that it has a method popitem which returns (key,value) pairs in LIFO order. So, you can also use it as a mapped 'stack'.
You not only get the full features of a dict
but also, some cool tricks.
Upvotes: 3
Reputation: 1298
Ordered dictionaries are just like regular dictionaries but they remember the order that items were inserted. When iterating over an ordered dictionary, the items are returned in the order their keys were first added.
So it only sorts by order of adding into the dict
You can build an OrderedDict order by key as follow,
orddict = OrderedDict(sorted(mydict.items(), key = lambda t: t[0]))
or simply as @ShadowRanger mentioned in comment
orddict = OrderedDict(sorted(d.items()))
If you want to order by value,
orddict = OrderedDict(sorted(mydict.items(), key = lambda t: t[1]))
More information in 8.3.5.1. OrderedDict Examples and Recipes
Upvotes: 2
Reputation: 2685
An OrderedDict
preserves the order elements were inserted:
>>> od = OrderedDict()
>>> od['c'] = 1
>>> od['b'] = 2
>>> od['a'] = 3
>>> od.items()
[('c', 1), ('b', 2), ('a', 3)]
>>> d = {}
>>> d['c'] = 1
>>> d['b'] = 2
>>> d['a'] = 3
>>> d.items()
[('a', 3), ('c', 1), ('b', 2)]
So an OrderedDict
does not order the elements for you, it preserves the order you give it.
If you want to "sort" a dictionary, you probably want
>>> sorted(d.items())
[('a', 1), ('b', 2), ('c', 3)]
Upvotes: 22