Back to Blog
Python

Python Dictionary Items Iteration Explained

python dictionary items iteration: Learn how to iterate over Python dictionary items with .items(), handle mutation errors, and understand view behavior and ordering.

dictionary iterationdict_items viewPython loopsruntime errorsdictionary views
Illustration of a Python dictionary being iterated with key-value pairs flowing through a loop

When you need to work with both the keys and the values of a Python dictionary, the items() method is the standard entry point. Python dictionary items iteration via .items() returns a view object that yields (key, value) tuples as the loop advances. Understanding what that view is, how it behaves under mutation, and how it differs from the older list-based behavior in Python 2 is what separates a correct loop from one that fails at runtime.

The Basic Pattern: Looping Over Key-Value Pairs

The most direct form of python dictionary items iteration is a for loop that unpacks each pair:

config = {"host": "localhost", "port": 8080, "debug": True} for key, value in config.items(): print(f"{key}: {value}")

The .items() call returns a dict_items view. Each iteration of the loop receives a tuple, and the key, value assignment unpacks it in place. This works for dictionaries of any size and is the pattern you will see in most production code.

If you do not need the key, you can iterate over .values() instead. If you do not need the value, iterating over the dictionary directly is equivalent to iterating over .keys():

for key in config: print(key)

That direct form is slightly faster than calling .keys() because it avoids the method lookup, though the difference is negligible for typical dictionary sizes.

Iterating Keys and Values Separately

When the loop only needs one side of the mapping, use the dedicated views:

for key in config.keys(): ... for value in config.values(): ...

Both return view objects that reflect the current dictionary state. If the dictionary changes after the view is created, the view reflects that change. This live behavior is useful when you want to scan a dictionary while another part of the code updates it, but it also means you must be careful about modifying the dictionary during the loop itself.

The RuntimeError Problem: Modifying a Dictionary During Iteration

Adding or removing keys while iterating over .items(), .keys(), or .values() raises RuntimeError: dictionary changed size during iteration:

for key, value in config.items(): if value is True: del config[key] # RuntimeError

The error occurs because the view tracks the dictionary's internal size. Mutating the dictionary invalidates the iteration state. The fix is to iterate over a snapshot instead:

for key in list(config.keys()): if config[key] is True: del config[key]

list(config.keys()) materializes the keys into a separate list before the loop starts, so deleting from the dictionary no longer affects the iteration sequence. An alternative is to collect the keys you want to remove and delete them after the loop:

to_remove = [k for k, v in config.items() if v is True] for k in to_remove: del config[k]

This second approach is often cleaner because it separates the decision logic from the mutation, and it works regardless of whether you iterate over a view or a snapshot.

Performance and Memory: Views vs Copies

The view-based behavior of .items(), .keys(), and .values() in Python 3 avoids allocating a new list for every call. For a large dictionary, that saves both time and memory compared with Python 2, where .items() returned a full list of tuples and .iteritems() was required for lazy iteration.

If you need a stable snapshot that will not change when the dictionary is modified, wrap the view in list():

snapshot = list(config.items())

Use a snapshot when you need to iterate multiple times over the same data while the dictionary may change, or when you need to index into the pairs. Otherwise, prefer the view directly.

Ordering Guarantees and Sorting

Since Python 3.7, dictionaries preserve insertion order, and .items() iterates in that order. Code that relies on this ordering is safe on modern Python, but it is not safe on Python 3.5 or earlier, where the order was arbitrary.

When insertion order is not what you need, sort the items explicitly:

for key, value in sorted(config.items()): ...

To sort by value rather than by key, supply a key function:

for key, value in sorted(config.items(), key=lambda item: item[1]): ...

For reverse order, pass reverse=True to sorted(). Sorting materializes the full list of pairs, so it is appropriate when the dictionary is small enough that the allocation cost is acceptable.

Destructuring Nested Dictionaries

When the values of a dictionary are themselves dictionaries, you can unpack the outer pair and then access the inner fields by key:

users = { "alice": {"role": "admin", "active": True}, "bob": {"role": "editor", "active": False}, } for username, details in users.items(): print(f"{username}: {details['role']}")

You cannot use nested tuple unpacking like for username, (role, active) in users.items() unless the value is a tuple or list with exactly two elements. For dict values, access fields by key inside the loop body. This keeps the code explicit and avoids assumptions about the internal shape of the value.

Compatibility Considerations Across Python Versions

The behavior of .items() differs significantly between Python 2 and Python 3:

MethodPython 2 behaviorPython 3 behavior
.items()Returns a new list of tuplesReturns a live view
.iteritems()Returns a lazy iteratorRemoved
.keys()Returns a listReturns a view
.values()Returns a listReturns a view

Code that relies on Python 2 semantics, such as calling .items() and then indexing into the result, will fail on Python 3 because views are not subscriptable. If you maintain code that must run on both versions, you need a compatibility layer, but most modern codebases target Python 3 only. The view behavior, combined with insertion-order guarantees since 3.7, makes .items() iteration predictable and memory-efficient in current Python.

python dictionary items iteration: Practical Usage and Code | RYUSLOG DEV