Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Using OrderedDict in Python
Sometimes you need a Python dictionary that remembers the order of its items. In the past, you had only one tool for solving this specific problem: Python’s OrderedDict. It’s a dictionary subclass specially designed to remember the order of items, which is defined by the insertion order of keys.
This changed in Python 3.6. The built-in dict class now keeps its items ordered as well. Because of that, many in the Python community now wonder if OrderedDict is still useful. A closer look at OrderedDict will uncover that this class still provides valuable features.
In this tutorial, you’ll learn how to:
- Create and use
OrderedDictobjects in your code - Identify the differences between
OrderedDictanddict - Understand the pros and cons of using
OrderedDictvsdict
With this knowledge, you’ll able to choose the dictionary class that best fits your needs when you want to preserve the order of items.
By the end of the tutorial, you’ll see an example of implementing a dictionary-based queue using OrderedDict, which would be more challenging if you used a regular dict object.
Free Bonus: Click here to get a Python Cheat Sheet and learn the basics of Python 3, like working with data types, dictionaries, lists, and Python functions.
Choosing Between OrderedDict and dict
For years, Python dictionaries were unordered data structures. Python developers were used to this fact, and they relied on lists or other sequences when they needed to keep their data in order. With time, developers found a need for a new type of dictionary, one that would keep its items ordered.
Back in 2008, PEP 372 introduced the idea of adding a new dictionary class to collections. Its main goal was to remember the order of items as defined by the order in which keys were inserted. That was the origin of OrderedDict.
Core Python developers wanted to fill in the gap and provide a dictionary that could preserve the order of inserted keys. That, in turn, allowed for a more straightforward implementation of specific algorithms that rely on this property.
OrderedDict was added to the standard library in Python 3.1. Its API is essentially the same as dict. However, OrderedDict iterates over keys and values in the same order that the keys were inserted. If a new entry overwrites an existing entry, then the order of items is left unchanged. If an entry is deleted and reinserted, then it will be moved to the end of the dictionary.
Python 3.6 introduced a new implementation of dict. This new implementation represents a big win in terms of memory usage and iteration efficiency. Additionally, the new implementation provides a new and somewhat unexpected feature: dict objects now keep their items in the same order they were introduced. Initially, this feature was considered an implementation detail, and the documentation advised against relying on it.
Note: In this tutorial, you’ll focus on the implementations of dict and OrderedDict that CPython provides.
In the words of Raymond Hettinger, core Python developer and coauthor of OrderedDict, the class was specially designed to keep its items ordered, whereas the new implementation of dict was designed to be compact and to provide fast iteration:
The current regular dictionary is based on the design I proposed several years ago. The primary goals of that design were compactness and faster iteration over the dense arrays of keys and values. Maintaining order was an artifact rather than a design goal. The design can maintain order but that is not its specialty.
In contrast, I gave
collections.OrderedDicta different design (later coded in C by Eric Snow). The primary goal was to have efficient maintenance of order even for severe workloads such as that imposed by thelru_cachewhich frequently alters order without touching the underlyingdict. Intentionally, theOrderedDicthas a design that prioritizes ordering capabilities at the expense of additional memory overhead and a constant factor worse insertion time.It is still my goal to have
collections.OrderedDicthave a different design with different performance characteristics than regular dicts. It has some order specific methods that regular dicts don’t have (such as amove_to_end()and apopitem()that pops efficiently from either end). TheOrderedDictneeds to be good at those operations because that is what differentiates it from regular dicts. (Source)
In Python 3.7, the items-ordered feature of dict objects was declared an official part of the Python language specification. So, from that point on, developers could rely on dict when they needed a dictionary that keeps its items ordered.
At this point, a question arises: Is OrderedDict still needed after this new implementation of dict? The answer depends on your specific use case and also on how explicit you want to be in your code.
At the time of writing, some features of OrderedDict still made it valuable and different from a regular dict:
- Intent signaling: If you use
OrderedDictoverdict, then your code makes it clear that the order of items in the dictionary is important. You’re clearly communicating that your code needs or relies on the order of items in the underlying dictionary. - Control over the order of items: If you need to rearrange or reorder the items in a dictionary, then you can use
.move_to_end()and also the enhanced variation of.popitem(). - Equality test behavior: If your code compares dictionaries for equality, and the order of items is important in that comparison, then
OrderedDictis the right choice.
There’s at least one more reason to continue using OrderedDict in your code: backward compatibility. Relying on regular dict objects to preserve the order of items will break your code in environments that run versions of Python older than 3.6.
It’s difficult to say if dict will fully replace OrderedDict soon. Nowadays, OrderedDict still offers interesting and valuable features that you might want to consider when selecting a tool for a given job.
Getting Started With Python’s OrderedDict
Python’s OrderedDict is a dict subclass that preserves the order in which key-value pairs, commonly known as items, are inserted into the dictionary. When you iterate over an OrderedDict object, items are traversed in the original order. If you update the value of an existing key, then the order remains unchanged. If you remove an item and reinsert it, then the item is added at the end of the dictionary.
Being a dict subclass means that it inherits all the methods a regular dictionary provides. OrderedDict also has additional features that you’ll learn about in this tutorial. In this section, however, you’ll learn the basics of creating and using OrderedDict objects in your code.
Creating OrderedDict Objects
Unlike dict, OrderedDict isn’t a built-in type, so the first step to create OrderedDict objects is to import the class from collections. There are several ways to create ordered dictionaries. Most of them are identical to how you create a regular dict object. For example, you can create an empty OrderedDict object by instantiating the class without arguments:
>>> from collections import OrderedDict
>>> numbers = OrderedDict()
>>> numbers["one"] = 1
>>> numbers["two"] = 2
>>> numbers["three"] = 3
>>> numbers
OrderedDict([('one', 1), ('two', 2), ('three', 3)])
In this case, you first import OrderedDict from collections. Then you create an empty ordered dictionary by instantiating OrderedDict without providing arguments to the constructor.
You can add key-value pairs to the dictionary by providing a key in square brackets ([]) and assigning a value to that key. When you reference numbers, you get an iterable of key-value pairs that holds items in the same order they were inserted into the dictionary.
You can also pass an iterable of items as an argument to the constructor of OrderedDict:
>>> from collections import OrderedDict
>>> numbers = OrderedDict([("one", 1), ("two", 2), ("three", 3)])
>>> numbers
OrderedDict([('one', 1), ('two', 2), ('three', 3)])
>>> letters = OrderedDict({("a", 1), ("b", 2), ("c", 3)})
>>> letters
OrderedDict([('c', 3), ('a', 1), ('b', 2)])
When you use a sequence, such as a list or a tuple, the order of the items in the resulting ordered dictionary matches the original order of items in the input sequence. If you use a set, like in the second example above, then the final order of items is unknown until the OrderedDict is created.
If you use a regular dictionary as an initializer to an OrderedDict object and you’re on Python 3.6 or beyond, then you get the following behavior:
Python 3.9.0 (default, Oct 5 2020, 17:52:02)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from collections import OrderedDict
>>> numbers = OrderedDict({"one": 1, "two": 2, "three": 3})
>>> numbers
OrderedDict([('one', 1), ('two', 2), ('three', 3)])
The order of items in the OrderedDict object matches the order in the original dictionary. On the other hand, if you’re using a Python version lower than 3.6, then the order of items is unknown:
Python 3.5.10 (default, Jan 25 2021, 13:22:52)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from collections import OrderedDict
>>> numbers = OrderedDict({"one": 1, "two": 2, "three": 3})
>>> numbers
OrderedDict([('one', 1), ('three', 3), ('two', 2)])
Since dictionaries in Python 3.5 don’t remember the order of their items, you don’t know the order in the resulting ordered dictionary until the object is created. From this point on, the order is maintained.
You can create an ordered dictionary by passing keyword arguments to the class constructor:
>>> from collections import OrderedDict
>>> numbers = OrderedDict(one=1, two=2, three=3)
>>> numbers
OrderedDict([('one', 1), ('two', 2), ('three', 3)])
Since Python 3.6, functions retain the order of keyword arguments passed in a call. So the order of the items in the above OrderedDict matches the order in which you pass the keyword arguments to the constructor. In earlier Python versions, that order is unknown.
Finally, OrderedDict also provides .fromkeys(), which creates a new dictionary from an iterable of keys and sets all its values to a common value:
>>> from collections import OrderedDict
>>> keys = ["one", "two", "three"]
>>> OrderedDict.fromkeys(keys, 0)
OrderedDict([('one', 0), ('two', 0), ('three', 0)])
In this case, you create an ordered dictionary using a list of keys as starting point. The second argument to .fromkeys() provides a single value to all the items in the dictionary.
Managing Items in an OrderedDict
Since OrderedDict is a mutable data structure, you can perform mutating operations on its instances. You can insert new items, update and remove existing items, and so on. If you insert a new item into an existing ordered dictionary, then the item is added to the end of the dictionary:
>>> from collections import OrderedDict
>>> numbers = OrderedDict(one=1, two=2, three=3)
>>> numbers
OrderedDict([('one', 1), ('two', 2), ('three', 3)])
>>> numbers["four"] = 4
>>> numbers
OrderedDict([('one', 1), ('two', 2), ('three', 3), ('four', 4)])
The newly added item, ('four', 4), is placed at the end of the underlying dictionary, so the order of the existing items remains unaffected, and the dictionary keeps the insertion order.
If you delete an item from an existing ordered dictionary and insert that same item again, then the new instance of the item is placed at the end of the dictionary:
>>> from collections import OrderedDict
>>> numbers = OrderedDict(one=1, two=2, three=3)
>>> del numbers["one"]
>>> numbers
OrderedDict([('two', 2), ('three', 3)])
>>> numbers["one"] = 1
>>> numbers
OrderedDict([('two', 2), ('three', 3), ('one', 1)])
If you remove the ('one', 1) item and insert a new instance of the same item, then the new item is added to the end of the underlying dictionary.
If you reassign or update the value of an existing key-value pair in an OrderedDict object, then the key maintains its position but gets a new value:
>>> from collections import OrderedDict
>>> numbers = OrderedDict(one=1, two=2, three=3)
>>> numbers["one"] = 1.0
>>> numbers
OrderedDict([('one', 1.0), ('two', 2), ('three', 3)])
>>> numbers.update(two=2.0)
>>> numbers
OrderedDict([('one', 1.0), ('two', 2.0), ('three', 3)])
If you update the value of a given key in an ordered dictionary, then the key isn’t moved but assigned the new value in place. In the same way, if you use .update() to modify the value of an existing key-value pair, then the dictionary remembers the position of the key and assigns the updated value to it.
Iterating Over an OrderedDict
Just like with regular dictionaries, you can iterate through an OrderedDict object using several tools and techniques. You can iterate over the keys directly, or you can use dictionary methods, such as .items(), .keys(), and .values():
>>> from collections import OrderedDict
>>> numbers = OrderedDict(one=1, two=2, three=3)
>>> # Iterate over the keys directly
>>> for key in numbers:
... print(key, "->", numbers[key])
...
one -> 1
two -> 2
three -> 3
>>> # Iterate over the items using .items()
>>> for key, value in numbers.items():
... print(key, "->", value)
...
one -> 1
two -> 2
three -> 3
>>> # Iterate over the keys using .keys()
>>> for key in numbers.keys():
... print(key, "->", numbers[key])
...
one -> 1
two -> 2
three -> 3
>>> # Iterate over the values using .values()
>>> for value in numbers.values():
... print(value)
...
1
2
3
The first for loop iterates over the keys of numbers directly. The other three loops use dictionary methods to iterate over the items, keys, and values of numbers.
Iterating in Reversed Order With reversed()
Another important feature that OrderedDict has provided since Python 3.5 is that its items, keys, and values support reverse iteration using reversed(). This feature was added to regular dictionaries in Python 3.8. So, if your code uses it, then your backward compatibility is much more restricted with normal dictionaries.
You can use reversed() with the items, keys, and values of an OrderedDict object:
>>> from collections import OrderedDict
>>> numbers = OrderedDict(one=1, two=2, three=3)
>>> # Iterate over the keys directly in reverse order
>>> for key in reversed(numbers):
... print(key, "->", numbers[key])
...
three -> 3
two -> 2
one -> 1
>>> # Iterate over the items in reverse order
>>> for key, value in reversed(numbers.items()):
... print(key, "->", value)
...
three -> 3
two -> 2
one -> 1
>>> # Iterate over the keys in reverse order
>>> for key in reversed(numbers.keys()):
... print(key, "->", numbers[key])
...
three -> 3
two -> 2
one -> 1
>>> # Iterate over the values in reverse order
>>> for value in reversed(numbers