Python Dictionaries: Hashing, Ordering & Performance Explained

Python Dictionaries: Hashing, Ordering & Performance Explained — cover image

Key Takeaways

  • Understand what is a dictionary in Python and why it is one of Python's most efficient data structures for storing related data.
  • Learn how dictionaries store elements as key-value pairs instead of using numeric indexes.
  • Explore dictionary syntax, literals, and different ways to create dictionaries.
  • Understand how key-value mapping differs from sequence indexing and why it enables fast lookups.
  • Build a strong foundation before learning dictionary methods, hashing, and performance.

Introduction

"A list tells you where the data is. A dictionary tells you what the data is."

Imagine storing a student's marks. You could use a list and remember that index 0 is the name, index 1 is the age, and index 2 is the score. But as the data grows, remembering positions quickly becomes difficult and error-prone.

A dictionary in Python solves this by storing information as key-value pairs, allowing values to be retrieved using meaningful keys instead of numeric indexes. This makes dictionaries ideal for representing records, configurations, grouped data, and lookup tables. Behind this simple interface lies one of Python's fastest data structures, powered by hashing for efficient lookups. Understanding how dictionaries are organized and why their keys follow specific rules will help you write cleaner, faster, and more maintainable Python programs.

What Is a Dictionary in Python?

A dictionary in Python is a built-in data structure that stores data as key-value pairs, where each key uniquely identifies an associated value. Unlike lists or tuples, dictionary elements are accessed using keys rather than numeric indexes.

student = {
    "name": "Alice",
    "age": 20,
    "course": "Python"
}

Here, "name", "age", and "course" are keys, while "Alice", 20, and "Python" are their corresponding values.

A dictionary is best suited for data where each value has a meaningful identifier rather than a fixed position.

Why Dictionaries Exist

Why Dictionaries Exist

Lists organize data by position. Dictionaries organize data by meaning.

Suppose you want to store information about a book.

Using a list:

book = ["Clean Code", "Robert C. Martin", 2008]

To access the author's name, you must remember that it is stored at index 1.

Using a dictionary:

book = {
    "title": "Clean Code",
    "author": "Robert C. Martin",
    "year": 2008
}

The value can now be retrieved using a descriptive key instead of a position.

Dictionaries exist because many real-world problems involve mapping one piece of information to another, such as:

  • Student ID → Student record
  • Country → Capital
  • Product ID → Product details
  • Username → User profile

This mapping model makes dictionaries more expressive and scalable than sequences for many applications.

Dictionary vs Sequence Indexing

Lists and tuples use integer indexes to locate elements.

languages = ["Python", "Java", "C++"]

print(languages[1])

Output

Java

A dictionary uses keys instead of positions.

student = {
    "name": "Alice",
    "age": 20
}

print(student["name"])

Output

Alice

This difference changes how data is organized.

Sequence (List/Tuple)

Dictionary

Access by numeric index

Access by key

Position identifies data

Key identifies data

Best for ordered collections

Best for mappings and records

When the identity of the data matters more than its position, a dictionary is usually the better choice.

Creating Dictionaries in Python

Python provides multiple ways to create dictionaries. The most common approach is using dictionary literals, although the built-in dict() function can also be used.

Dictionary Syntax

The syntax of a dictionary in Python uses curly braces {} with each key mapped to a value using a colon (:). Individual key-value pairs are separated by commas.

student = {
    "name": "Alice",
    "age": 20,
    "course": "Python"
}

General syntax:

{
    key1: value1,
    key2: value2,
    ...
}

Each key in a dictionary must be unique. If the same key appears more than once, the last assigned value replaces the previous one.

Dictionary Literals

A dictionary literal is the most direct way to create a dictionary.

Example:

car = {
    "brand": "Tesla",
    "model": "Model 3",
    "year": 2024
}

Dictionary literals are concise, readable, and the preferred approach when the key-value pairs are known in advance.

Empty Dictionaries

An empty dictionary can be created in two ways.

Using curly braces:

student = {}

Using the dict() constructor:

student = dict()

Both create an empty dictionary that can be populated later.

student["name"] = "Alice"
student["age"] = 20

Creating an empty dictionary first is useful when data is collected dynamically, such as user input, API responses, or values generated during program execution.

Working with Dictionaries

Once a dictionary is created, you can retrieve, add, update, and remove key-value pairs. Unlike lists, dictionary operations are performed using keys rather than numeric indexes, making data easier to access and manage.

Accessing Dictionary Values

Values in a dictionary are retrieved using their corresponding keys.

student = {
    "name": "Alice",
    "age": 20
}

print(student["name"])

Output

Alice

If the specified key does not exist, Python raises a KeyError.

Using get()

The get() method retrieves a value without raising an error when the key is missing. Instead, it returns None or a default value if one is provided.

student = {
    "name": "Alice",
    "age": 20
}

print(student.get("age"))
print(student.get("course"))

Output

20
None

You can also specify a default value.

print(student.get("course", "Not Found"))

Output

Not Found

Use get() when a key may not exist and you want to avoid handling a KeyError.

Dictionary Operations

Dictionaries support common operations for managing key-value pairs.

Adding Key-Value Pairs

Assigning a value to a new key inserts a new entry into the dictionary.

student = {
    "name": "Alice"
}
student["age"] = 20
print(student)

Output

{'name': 'Alice', 'age': 20}

Updating Dictionary Values

Assigning a value to an existing key replaces the previous value.

student = {
    "name": "Alice",
    "age": 20
}
student["age"] = 21
print(student)

Output

{'name': 'Alice', 'age': 21}

This is the most common way to update a dictionary in Python.

Removing Entries

Python provides multiple ways to remove dictionary entries.

Using del

student = {
    "name": "Alice",
    "age": 20
}
del student["age"]
print(student)

Output

{'name': 'Alice'}

Using pop()

The pop() method removes a key and returns its associated value.

student = {
    "name": "Alice",
    "age": 20
}
age = student.pop("age")

print(age)
print(student)

Output

20
{'name': 'Alice'}

Use pop() when you need both to remove an entry and preserve its value.

Dictionary Methods

Python provides several built-in dictionary methods for accessing and modifying dictionary data efficiently.

keys()

Returns a dynamic view of all dictionary keys.

student = {
    "name": "Alice",
    "age": 20
}
print(student.keys())

Output

dict_keys(['name', 'age'])

values()

Returns a view containing all dictionary values.

print(student.values())

Output

dict_values(['Alice', 20])

items()

Returns each key-value pair as a tuple.

print(student.items())

Output

dict_items([('name', 'Alice'), ('age', 20)])

The items() method is commonly used when both keys and values are needed during iteration.

update()

Merges another dictionary or iterable of key-value pairs into the existing dictionary.

student = {
    "name": "Alice"
}
student.update({"age": 20, "course": "Python"})
print(student)

Output

{'name': 'Alice', 'age': 20, 'course': 'Python'}

If a key already exists, its value is replaced.

pop()

Removes the specified key and returns its value.

student = {
    "name": "Alice",
    "age": 20
}
print(student.pop("age"))

Output

20

If the key does not exist and no default value is provided, pop() raises a KeyError.

setdefault()

Returns the value for a key if it exists. Otherwise, it inserts the key with the specified default value.

student = {
    "name": "Alice"
}
student.setdefault("age", 20)
print(student)

Output

{'name': 'Alice', 'age': 20}

Unlike update(), setdefault() does not overwrite an existing value.

Iterating Over Dictionaries

Iteration processes dictionary elements one key-value pair at a time. Depending on your requirement, you can iterate over keys, values, or both.

Iterating Over Keys

By default, iterating over a dictionary returns its keys.

student = {
    "name": "Alice",
    "age": 20
}

for key in student:
    print(key)

Output

name
age

The same result can be obtained explicitly using keys().

for key in student.keys():
    print(key)

Iterating Over Values

Use values() when only the stored values are required.

for value in student.values():
    print(value)

Output

Alice
20

Iterating Over Key-Value Pairs

The items() method returns both the key and its associated value, making it the most efficient way to iterate over complete dictionary entries.

for key, value in student.items():
    print(key, value)

Output

name Alice
age 20

When you need access to both parts of a dictionary entry, iterating with items() is clearer and more efficient than looking up each value separately using its key.

How Dictionaries Work Under the Hood

A dictionary appears to be a simple collection of key-value pairs, but internally it is designed for fast lookups, updates, and insertions. This efficiency comes from hashing, which allows Python to locate values using keys instead of searching through every element.

Why Dictionary Keys Must Be Hashable

Every dictionary key must be hashable. A hashable object has a hash value that remains unchanged during its lifetime, allowing Python to reliably locate the associated value.

Common hashable objects include:

  • Strings
  • Integers
  • Floats
  • Tuples (if all their elements are hashable)
student = {
    "name": "Alice",
    101: "Student ID",
    (10, 20): "Coordinate"
}

Objects such as lists, dictionaries, and sets are not hashable because they are mutable.

student = {
    [1, 2]: "Numbers"
}

Output

TypeError: unhashable type: 'list'

Why Immutable Keys Matter

Why Immutable Keys Matter

A dictionary uses a key's hash value to determine where its corresponding value is stored. If a key could change after being inserted, its hash value would also change, making the entry difficult or impossible to locate.

This is why dictionary keys must be immutable.

For example, a string can safely be used as a key because its value cannot change after creation.

employee = {
    "id": 101
}

By ensuring keys remain immutable, Python can perform consistent and reliable dictionary lookups.

How Dictionary Lookup Works

Unlike a list, a dictionary does not search entries one by one.

When you access a value, Python:

  • Computes the key's hash value.
  • Uses that hash to locate the corresponding storage position.
  • Returns the associated value.
student = {
    "name": "Alice",
    "age": 20
}

print(student["name"])

Instead of checking every key sequentially, Python jumps directly to the location determined by the hash value. This is why dictionary lookups are typically much faster than searching through a list.

Insertion Order in Modern Python

Before Python 3.7, dictionaries did not guarantee that items would be returned in the order they were added.

Starting with Python 3.7, dictionaries preserve insertion order.

student = {
    "name": "Alice",
    "age": 20,
    "course": "Python"
}

print(student)

Output

{'name': 'Alice', 'age': 20, 'course': 'Python'}

Although dictionaries preserve insertion order, they are not automatically sorted. If keys need to appear in sorted order, they must be sorted explicitly.

Dictionary Aliasing and Copying

Assigning one dictionary to another variable creates a new reference to the same dictionary, not a copy.

student = {
    "name": "Alice"
}

data = student

data["age"] = 20

print(student)

Output

{'name': 'Alice', 'age': 20}

Both variables refer to the same dictionary, so changes made through one reference are visible through the other.

To create an independent dictionary, use the copy() method.

student = {
    "name": "Alice"
}

data = student.copy()

data["age"] = 20

print(student)
print(data)

Output

{'name': 'Alice'}
{'name': 'Alice', 'age': 20}

The copy() method creates a shallow copy, meaning the outer dictionary is copied while nested mutable objects are still shared.

Time Complexity of Dictionary Operations

Dictionary performance comes from hash-based lookups. Most common operations complete in constant time on average, regardless of the dictionary's size.

Operation

Average Complexity

Reason

Access (dict[key])

O(1)

Hash value directly locates the entry.

Insert

O(1)

New key-value pair is placed using its hash.

Update

O(1)

Existing value is replaced using the key's hash.

Delete

O(1)

The key is located through its hash before removal.

Membership (key in dict)

O(1)

Checks whether the hashed key exists.

Iteration

O(n)

Every key-value pair is visited once.

Rather than memorizing these complexities, remember the underlying idea:

  • Dictionary operations are fast because Python uses hashes instead of sequential searches.
  • Lookup speed depends on keys being hashable and immutable.
  • Iterating over a dictionary still requires visiting every entry, so it scales linearly with the number of items.

Dictionary Comprehensions

A dictionary comprehension creates a new dictionary by combining iteration and an optional condition into a single expression. It offers a concise alternative to building dictionaries with a loop.

Syntax

{key_expression: value_expression for item in iterable if condition}

Example:

numbers = [1, 2, 3, 4]

squares = {n: n * n for n in numbers}

print(squares)

Output

{1: 1, 2: 4, 3: 9, 4: 16}

Dictionary comprehensions are useful for transforming data, creating lookup tables, and filtering key-value pairs while keeping the code concise.

Nested Dictionaries

A nested dictionary is a dictionary whose values are themselves dictionaries. It is commonly used to model structured or hierarchical data.

students = {
    "S101": {
        "name": "Alice",
        "marks": 92
    },
    "S102": {
        "name": "Bob",
        "marks": 88
    }
}

Accessing nested values:

print(students["S101"]["marks"])

Output

92

Nested dictionary Python structures are useful for representing records, JSON data, application settings, and API responses.

Real-World Uses of Dictionaries

Because dictionaries map keys to values, they naturally model many real-world problems.

Counting Items

Dictionaries efficiently track the frequency of elements.

text = "banana"

count = {}

for ch in text:
    count[ch] = count.get(ch, 0) + 1

print(count)

Grouping Data

Dictionaries can organize related values under a common key.

employees = {
    "Engineering": ["Alice", "Bob"],
    "Sales": ["John"]
}

Configuration Settings

Applications often store configuration values in dictionaries.

config = {
    "theme": "dark",
    "language": "English"
}

Dispatch Tables

A dictionary can map commands to functions, eliminating long chains of conditional statements.

operations = {
    "add": add,
    "subtract": subtract
}

Modeling Records

Dictionaries are widely used to represent objects with named attributes.

student = {
    "name": "Alice",
    "age": 20,
    "course": "Python"
}

Practical Examples

Sample Dictionary

student = {
    "name": "Alice",
    "age": 20,
    "marks": 95
}

Word Frequency Counter

sentence = "python python data"

frequency = {}

for word in sentence.split():
    frequency[word] = frequency.get(word, 0) + 1

print(frequency)

Output

{'python': 2, 'data': 1}

Student Marks Lookup

marks = {
    "Alice": 95,
    "Bob": 88
}

print(marks["Bob"])

Output

88

These examples demonstrate how dictionaries simplify data retrieval using meaningful keys instead of numeric indexes.

Best Practices

  • Use descriptive, meaningful keys.
  • Keep dictionary keys immutable and hashable.
  • Use get() when a key may not exist.
  • Use items() when both keys and values are needed during iteration.
  • Use dictionary comprehensions for simple transformations.
  • Create a copy before modifying a shared dictionary.
  • Choose dictionaries when data naturally forms key-value relationships.

Common Mistakes

Using Mutable Objects as Keys

Lists, dictionaries, and sets cannot be used as dictionary keys because they are not hashable.

Accessing Missing Keys Directly

student["course"]

raises a KeyError if the key doesn't exist. Use get() when the key is optional.

Confusing Assignment with Copying

a = {"name": "Alice"}
b = a

Both variables reference the same dictionary. Use copy() to create a separate dictionary.

Modifying a Dictionary While Iterating

Adding or removing entries during iteration can raise a RuntimeError. If modifications are required, iterate over a copy of the keys.

Assuming Dictionaries Are Sorted

Dictionaries preserve insertion order, but they do not sort keys automatically. Use sorted() when sorted output is required.

Summary

A dictionary in Python is a powerful mapping data structure that stores information as key-value pairs, making data retrieval fast and intuitive. In this guide, you learned how to create dictionaries, perform common dictionary operations, use built-in methods, iterate over entries, and model structured data with nested dictionaries. You also explored how hashing enables efficient lookups, why dictionary keys must be immutable and hashable, how insertion order works, and how aliasing and copying affect dictionary behavior. Understanding these concepts helps you choose dictionaries confidently and use them effectively in real-world Python programs.

Frequently Asked Questions

1. Define Dictionary in Python

If you need to define dictionary in Python, it can be described as:

A dictionary is a mutable mapping data structure that stores elements as unique key-value pairs.

Unlike sequences, a dictionary retrieves data using keys, making it easy to locate and update specific values.

Example:

employee = {
    "id": 101,
    "name": "John",
    "department": "Engineering"
}

Each key maps directly to a value, allowing information to be retrieved without knowing its position.

2. Are dictionaries mutable in Python?

Yes. Dictionaries are mutable, so entries can be added, updated, and removed after creation.

Why must dictionary keys be hashable?

Python uses the key's hash value to locate its associated value efficiently. Mutable objects cannot be used as keys because their hash values could change.

3. Is a dictionary ordered in Python?

Yes. Since Python 3.7, dictionaries preserve the order in which key-value pairs are inserted.

4. What is the difference between get() and direct indexing?

dict[key] raises a KeyError if the key is missing, whereas get() returns None or a specified default value.

5. When should I use a dictionary instead of a list?

Use a dictionary when data is identified by keys rather than positions, such as student records, configuration settings, or product information.