Of course! This is a fundamental concept in modern Python that has changed significantly between versions. Here’s a complete guide to dictionary ordering in Python.
The Short Answer (TL;DR)
In Python 3.7 and newer, dictionaries preserve insertion order. This is a guaranteed language feature, not just an implementation detail.
- What you put in is what you get out, when you iterate over the keys, values, or items.
- This applies to all modern versions of Python (3.7, 3.8, 3.9, 3.10, 3.11, 3.12).
The Detailed Explanation
Let's break it down by Python version and look at the key concepts.
Python 3.7 and Later: Guaranteed Insertion Order
This is the current standard. You can rely on the fact that a dictionary will remember the order in which you added its key-value pairs.
Example:
# In Python 3.7+
user_info = {}
user_info['name'] = 'Alice'
user_info['age'] = 30
user_info['city'] = 'New York'
# When you iterate, the order is preserved
for key in user_info:
print(key)
# Output:
# name
# age
# city
# You can also see the order when you print the dict directly
print(user_info)
# Output (on most Python versions):
# {'name': 'Alice', 'age': 30, 'city': 'New York'}
Why did this change?
- Python 3.6: The order preservation was an implementation detail of the CPython interpreter. It worked, but you shouldn't have relied on it in your code, as other implementations (like PyPy or Jython) might not have behaved the same way.
- Python 3.7: The order preservation became a guaranteed feature of the language. The official documentation was updated to state that
dictobjects now preserve insertion order.
Python 3.6 and Earlier: No Order Guarantee
In versions before 3.7, dictionaries were unordered. The order of keys when you iterated over a dictionary was essentially arbitrary and could change between different runs of your program, or even between different operations on the same dictionary.
Example (simulating old behavior):
# In Python 3.5 or earlier
# The order you see might be different every time you run this.
my_dict = {'c': 3, 'a': 1, 'b': 2}
for key in my_dict:
print(key)
# Possible Output 1:
# c
# a
# b
# Possible Output 2 (if the dictionary was rebuilt):
# a
# b
# c
Why were they unordered? Dictionaries historically used a hash table for fast lookups. The order of items in the hash table depended on the hash values of the keys and the internal state of the hash table, which was not related to the insertion order.
How Does Ordering Affect Dictionary Operations?
Order preservation has some interesting side effects.
a) Deleting and Re-adding a Key
If you delete a key and then add it back, it goes to the end of the dictionary.
d = {'a': 1, 'b': 2, 'c': 3}
print(d) # {'a': 1, 'b': 2, 'c': 3}
del d['b']
print(d) # {'a': 1, 'c': 3}
d['b'] = 2 # 'b' is added to the end
print(d) # {'a': 1, 'c': 3, 'b': 2}
b) Updating an Existing Key
If you update the value of an existing key, its position in the order does not change.
d = {'a': 1, 'b': 2, 'c': 3}
print(d) # {'a': 1, 'b': 2, 'c': 3}
d['b'] = 99 # The key 'b' already exists
print(d) # {'a': 1, 'b': 99, 'c': 3} # Order is unchanged
c) dict.update() Method
The update() method respects the order of both dictionaries. New keys from the updated dictionary are appended to the end.
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'a': 100} # Note 'a' already exists in d1
d1.update(d2)
print(d1)
# Output:
# {'a': 100, 'b': 2, 'c': 3}
# 'a' was updated in-place (order stays)
# 'c' was added to the end
What About Sorting a Dictionary?
Sometimes you don't want the original insertion order; you want to sort the dictionary by its keys or values. Since dictionaries themselves are not "sorted" objects, the standard way to do this is to create a new dictionary from a sorted list of its items.
The sorted() function is your best friend here.
Example: Sorting by Keys
unsorted_dict = {'banana': 3, 'apple': 1, 'pear': 2, 'orange': 4}
# sorted() on a dict iterates over its keys by default
sorted_keys = sorted(unsorted_dict)
print(sorted_keys)
# Output: ['apple', 'banana', 'orange', 'pear']
# Create a new dictionary from the sorted key-value pairs
sorted_by_key = {k: unsorted_dict[k] for k in sorted_keys}
print(sorted_by_key)
# Output: {'apple': 1, 'banana': 3, 'orange': 4, 'pear': 2}
A more common and "Pythonic" way to do this is with dict.items() and a lambda function.
unsorted_dict = {'banana': 3, 'apple': 1, 'pear': 2, 'orange': 4}
# Sort by key (the first element of each tuple)
sorted_by_key = dict(sorted(unsorted_dict.items(), key=lambda item: item[0]))
print(sorted_by_key)
# Output: {'apple': 1, 'banana': 3, 'orange': 4, 'pear': 2}
Example: Sorting by Values
unsorted_dict = {'banana': 3, 'apple': 1, 'pear': 2, 'orange': 4}
# Sort by value (the second element of each tuple)
sorted_by_value = dict(sorted(unsorted_dict.items(), key=lambda item: item[1]))
print(sorted_by_value)
# Output: {'apple': 1, 'pear': 2, 'banana': 3, 'orange': 4}
Other "Ordered" Dictionary Types
While standard dict is now ordered, you might still encounter these two types.
collections.OrderedDict
This was the pre-3.7 solution for creating ordered dictionaries. It's part of the collections module.
- In Python 3.7+: You don't need
OrderedDictfor simple insertion order. A standarddictis faster and more idiomatic. - When to use it today:
OrderedDictstill has one feature thatdictdoesn't: it remembers the order in which items were last inserted or moved. This can be useful in specific caching scenarios.
from collections import OrderedDict
# A regular dict (Python 3.7+) also remembers insertion order
d = {}
d['a'] = 1
d['b'] = 2
d['c'] = 3
d.move_to_end('a') # This method doesn't exist on a regular dict!
print(d) # Raises AttributeError
# An OrderedDict does this correctly
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od.move_to_end('a') # Moves 'a' to the end
print(od)
# Output: OrderedDict([('b', 2), ('c', 3), ('a', 1)])
dict vs. defaultdict vs. OrderedDict
dict: Standard, unordered (pre-3.6), ordered by insertion (3.7+).defaultdict: A subclass ofdictthat calls a factory function to supply a default value for a missing key. It is ordered just like adict.OrderedDict: A subclass ofdictthat remembers insertion order (and move order). Slower than a standarddict.
Summary Table
| Feature | Python 3.7+ | Python 3.6 and Earlier |
|---|---|---|
| Order Guarantee | Guaranteed (Language Feature) | Not Guaranteed (Implementation Detail) |
dict Type |
Preserves insertion order. | Unordered. |
del and re-add |
Item moves to the end. | Behavior is undefined. |
update() |
Appends new keys to the end. | Behavior is undefined. |
| Primary Use Case | The default, go-to dictionary. | Use collections.OrderedDict if order is critical. |
| Sorting | Use sorted(dict.items(), key=...) to create a new sorted dict. |
Same method. |
