Back to Blog
Python

Python Dictionary: Practical Usage and Performance

python dictionary: Learn how to create, access, and manipulate Python dictionaries, understand their performance characteristics, and avoid common pitfalls in real-wor...

dictkey-value storagehash tabledictionary methodsperformance
Illustration of a Python dictionary as a set of key-value pairs with a hash table metaphor.

A Python dictionary is a built-in data structure that stores key-value pairs. It is one of the most frequently used tools in Python because it provides fast lookups, flexible key types, and a readable syntax. Understanding how dictionaries work internally and where their strengths and limitations lie helps you write code that is both efficient and maintainable.

Creating and Accessing Dictionaries

Dictionaries can be created with curly braces or the dict() constructor. The literal syntax is the most common and often the most readable:

person = {"name": "Ada", "role": "engineer"} empty = {} from_pairs = dict([("name", "Ada"), ("role", "engineer")])

Accessing a value by key is straightforward with square brackets, but this raises a KeyError if the key is missing. For cases where a missing key should not be an error, the .get() method returns None or a provided default:

role = person["role"] missing = person.get("department") # None fallback = person.get("department", "unknown")

The .setdefault() method is useful when you want to insert a default value only if the key is absent. This is common when building counts or grouping data:

counts = {} for word in words: counts[word] = counts.setdefault(word, 0) + 1

Common Operations and Methods

Dictionaries support a rich set of methods for adding, updating, and removing items. The update() method merges another mapping or iterable of key-value pairs into the dictionary, overwriting existing keys:

person.update({"location": "London"}) person.update([("years", 5)])

To remove a key, pop() returns the value and removes the key, while del simply removes it. The popitem() method removes and returns the last inserted key-value pair in Python 3.7+ where insertion order is guaranteed.

Membership testing with in is efficient and is the preferred way to check whether a key exists:

if "name" in person: print(person["name"])

Iteration and View Objects

Iterating over a dictionary yields its keys by default. To iterate over values or key-value pairs, use the .values() and .items() methods. These return view objects that reflect changes to the dictionary in real time:

for key in person: print(key) for value in person.values(): print(value) for key, value in person.items(): print(key, value)

View objects are not lists, so they cannot be indexed directly. If you need an index, convert them to a list first. This is a common source of confusion for developers coming from other languages.

Performance Characteristics and Memory Behavior

Dictionaries are implemented as hash tables. This gives average-case O(1) time complexity for lookup, insertion, and deletion. The hash of each key determines its storage location, so keys must be hashable. Immutable types like strings, numbers, and tuples are hashable; lists and other mutable containers are not.

Because hash tables rely on a good hash function, collisions degrade performance to O(n) in the worst case. Python's hash function for strings is randomized per process to mitigate denial-of-service attacks, but for most applications the average-case behavior is what matters.

Memory usage is higher than a list of tuples because the hash table allocates extra space to keep load factors low. When a dictionary grows, it may need to rehash all keys, which is an O(n) operation. This is rarely a problem in practice, but it explains why creating a dictionary with a known size using dict.fromkeys() or a comprehension can be slightly more efficient than repeated insertion.

When to Use a Dictionary Instead of a List or Set

Choosing between a dictionary, list, and set depends on the operation you need to perform. A dictionary is the right choice when you need to associate a value with a unique key. A set is appropriate when you only need to track membership without an associated value. A list is better when order and duplicates matter.

The following table summarizes the decision criteria:

NeedRecommended Structure
Map keys to valuesDictionary
Track unique itemsSet
Preserve order and duplicatesList

For example, if you need to count occurrences of words, a dictionary mapping each word to its count is natural. If you only need to know which words appear, a set is sufficient. If you need to process words in the order they appear, a list preserves that order.

Common Pitfalls and Edge Cases

One frequent mistake is using a mutable default value in a function signature. This is not specific to dictionaries but appears often when a dictionary is used as a default argument:

def add_item(item, storage={}): storage[item] = True return storage

The default dictionary is created once and shared across all calls. The correct pattern is to use None and create a new dictionary inside the function.

Another pitfall is assuming dictionaries preserve insertion order in older Python versions. While this is guaranteed from Python 3.7 onward, code that relies on order should not be run on older interpreters without checking the version.

When copying a dictionary, a shallow copy with dict.copy() or copy.copy() shares the same value objects. If the values are mutable, changes to those values will affect both dictionaries. Use copy.deepcopy() when you need independent nested structures.

Finally, be aware that dictionary keys must be hashable. If you need to use a list as a key, convert it to a tuple first. This is a common workaround that preserves the logical content while making the key immutable.

Advanced Usage: Dictionary Comprehensions and Merging

Dictionary comprehensions provide a concise way to build dictionaries from iterables. For example, creating a mapping from numbers to their squares:

squares = {x: x**2 for x in range(10)}

Merging two dictionaries can be done with the {**a, **b} syntax or the | operator in Python 3.9+. The | operator is more readable and also supports in-place merging with |=:

merged = {**defaults, **user_settings} merged = defaults | user_settings

These features make dictionary manipulation more expressive and reduce the need for explicit loops. When working with large data sets, consider whether a comprehension or a generator expression is more memory-efficient, but remember that a generator does not produce a dictionary until consumed.

python dictionary: Practical Usage and Code Examples | RYUSLOG DEV