← Python Code Memory & Data Structures
Browse Python Concepts

Dict Ordering Guarantees in Python 3.7+

Mental Model

Think of a modern Python dictionary's order as a chronological log of key insertions. Updating a key doesn't change its log entry's position, but deleting an entry removes it from the log, and re-adding it appends a new entry to the end of the log.

Rule: When key order must be preserved, update values in place instead of deleting and re-inserting the key.

The Setup

You are writing a metadata serialization module that exports key-value configurations to flat arrays, expecting the payload order to remain identical to the definition order. An automated script updates these entries dynamically over time.

What Does This Print?

Broken code
Python
config = {"a": 1, "b": 2, "c": 3}

# Update configurations
config["a"] = 100
del config["b"]
config["b"] = 200

print("Keys ordered after modification:", list(config.keys()))
Predict the exact key order output from the modified dictionary. Does b retain its original second slot or shift to the end?

The Output

What actually happens
Keys ordered after modification: ['a', 'c', 'b']

The key order is ['a', 'c', 'b']. Re-adding deleted keys places them at the end of the insertion queue, while updating an existing key maintains its relative position.

Why Python Does This

Since Python 3.6 (and guaranteed in 3.7+), dicts use a split-table representation designed by Raymond Hettinger. Instead of a sparse array containing pointers to keys and values, CPython utilizes an integer array (indices) and a dense array (entries) holding the actual hash, key, and value in order of arrival. Updating a key updates its value inline in the dense array. Deleting a key removes its reference and marks the index slot as dummy, and adding it back appends it to the end of the dense array, altering the relative iteration order.

The Fix

Corrected pattern
Python
config = {"a": 1, "b": 2, "c": 3}

config["a"] = 100
# Update b's value in place — no delete, so the key retains its original position
config["b"] = 200

print("Keys ordered after modification:", list(config.keys()))
# Output: ['a', 'b', 'c']  — b stays in slot 2

Updating a key in place (config["b"] = 200) modifies the value stored in the existing dense-array entry without touching the indices array. The key's slot in the insertion sequence never changes. Deleting and re-inserting, by contrast, removes the entry from the dense array and appends a new one at the end, reordering the key.

How This Fails in Real Systems

A dynamic API gateway payload signed outgoing requests using a SHA256 signature generated over dictionary keys. When updates were deployed, some key modifications caused unexpected reordering, breaking the deterministic hashing mechanism and leading to 5% of webhooks being rejected.

Key Takeaway

When key order must be preserved, update values in place instead of deleting and re-inserting the key.
Common mistake: Developers assume that after deleting and re-adding a key, a dictionary will maintain the key's original position from its first insertion.