Python Dictionaries

Python Dictionaries

Dictionaries are Python’s mapping type that store key-value pairs. They provide fast lookups and are essential for structured data. This tutorial covers dictionary creation, operations, and best practices.

Creating Dictionaries

Dictionaries map keys to values using curly braces or the dict() constructor.

Basic Dictionary Creation

# Empty dictionary
empty_dict = {}

# Dictionary with key-value pairs
person = {
    "name": "Alice",
    "age": 30,
    "city": "New York"
}

# Using dict() constructor
person2 = dict(name="Bob", age=25, city="Chicago")

# From key-value pairs
pairs = [("x", 10), ("y", 20)]
coordinates = dict(pairs)

print(person)
print(person2)

Dictionaries use curly braces and colons. See the dict constructor for more ways to create them.

Dictionary Keys

Keys must be immutable types like strings, numbers, or tuples.

# Valid keys
valid_dict = {
    "string_key": "value",
    42: "number key",
    (1, 2): "tuple key",
    True: "boolean key"
}

# Invalid keys (would cause error)
# invalid_dict = {[1, 2]: "list key"}  # Lists are mutable

print(valid_dict["string_key"])

Keys must be hashable. Learn about hashable types in Python.

Accessing Dictionary Values

Use keys to retrieve values, with methods for safe access.

Direct Access

person = {"name": "Alice", "age": 30, "city": "New York"}

# Access by key
name = person["name"]
age = person["age"]

print(f"Name: {name}, Age: {age}")

# person["salary"]  # KeyError if key doesn't exist

Direct access raises KeyError for missing keys. Use safe methods instead.

Safe Access with get()

person = {"name": "Alice", "age": 30}

# Safe access with default
name = person.get("name")           # "Alice"
salary = person.get("salary")       # None
salary_with_default = person.get("salary", 50000)  # 50000

print(f"Salary: {salary_with_default}")

The get() method prevents KeyErrors. See dict.get() documentation.

Modifying Dictionaries

Dictionaries are mutable - you can add, update, and remove entries.

Adding and Updating

person = {"name": "Alice", "age": 30}

# Add new key-value pair
person["email"] = "alice@example.com"

# Update existing value
person["age"] = 31

# Update multiple values
person.update({"city": "Boston", "job": "Engineer"})

print(person)

Use assignment to add or update. The update() method merges dictionaries.

Removing Items

person = {"name": "Alice", "age": 30, "city": "New York", "email": "alice@example.com"}

# Remove specific key
del person["email"]

# Remove and return value
city = person.pop("city")
job = person.pop("job", "Unemployed")  # With default

# Remove last item (Python 3.7+ maintains insertion order)
last_key, last_value = person.popitem()

print(f"Removed city: {city}")
print(f"Remaining: {person}")

Multiple ways to remove items. Check dict methods for all options.

Dictionary Methods

Essential methods for working with dictionaries.

Keys, Values, and Items

person = {"name": "Alice", "age": 30, "city": "New York"}

# Get all keys
keys = person.keys()        # dict_keys(['name', 'age', 'city'])
keys_list = list(keys)      # Convert to list

# Get all values
values = person.values()    # dict_values(['Alice', 30, 'New York'])

# Get all key-value pairs
items = person.items()      # dict_items([('name', 'Alice'), ('age', 30), ('city', 'New York')])

print(f"Keys: {keys_list}")
print(f"Has name: {'name' in keys}")

These return view objects that update automatically. See dictionary views.

Other Useful Methods

person = {"name": "Alice", "age": 30}

# Clear all items
person_copy = person.copy()
person_copy.clear()

# Check if empty
is_empty = len(person) == 0

# Set default value if key missing
person.setdefault("city", "Unknown")

print(f"Length: {len(person)}")
print(f"Is empty: {is_empty}")

These methods help with common operations. Explore all dict methods.

Dictionary Comprehensions

Create dictionaries concisely using comprehensions.

# Basic comprehension
squares = {x: x**2 for x in range(5)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# From two lists
keys = ["name", "age", "city"]
values = ["Alice", 30, "New York"]
person = {k: v for k, v in zip(keys, values)}

# With condition
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}

print(person)
print(even_squares)

Comprehensions work like list comprehensions but create key-value pairs. See dict comprehensions.

Nested Dictionaries

Dictionaries can contain other dictionaries for complex data structures.

# Nested dictionary
company = {
    "name": "Tech Corp",
    "employees": {
        "alice": {"age": 30, "role": "developer"},
        "bob": {"age": 25, "role": "designer"}
    },
    "departments": ["engineering", "design", "sales"]
}

# Access nested data
alice_age = company["employees"]["alice"]["age"]
print(f"Alice's age: {alice_age}")

# Add to nested dict
company["employees"]["charlie"] = {"age": 35, "role": "manager"}

Nested structures are common for hierarchical data. Use collections for more complex nesting.

Defaultdict

A special dictionary that provides default values for missing keys.

from collections import defaultdict

# Defaultdict with list default
word_counts = defaultdict(list)
word_counts["python"].append("snake")
word_counts["python"].append("language")

# Defaultdict with int default (common for counters)
char_count = defaultdict(int)
for char in "hello world":
    char_count[char] += 1

print(word_counts["python"])  # ['snake', 'language']
print(char_count["l"])        # 3

Defaultdict simplifies common patterns. See collections.defaultdict.

OrderedDict

Maintains insertion order (default in Python 3.7+, but OrderedDict available for older versions).

from collections import OrderedDict

# OrderedDict (maintains order)
ordered = OrderedDict()
ordered["first"] = 1
ordered["second"] = 2
ordered["third"] = 3

print(list(ordered.keys()))  # ['first', 'second', 'third']

# Move to end
ordered.move_to_end("first")
print(list(ordered.keys()))  # ['second', 'third', 'first']

OrderedDict is useful when order matters. Check collections.OrderedDict.

Dictionary Performance

Dictionaries provide O(1) average-case lookup time, making them very fast.

import time

# Large dictionary for performance test
large_dict = {i: i*2 for i in range(100000)}

# Fast lookups
start = time.time()
for i in range(1000):
    _ = large_dict[i]
end = time.time()

print(f"1000 lookups took: {end - start:.4f} seconds")

Dictionaries are optimized for speed. Learn about dict implementation details.

Common Patterns

Counting with Dictionaries

# Count word frequencies
text = "the quick brown fox jumps over the lazy dog"
words = text.split()
word_count = {}

for word in words:
    word_count[word] = word_count.get(word, 0) + 1

print(word_count)

Dictionaries are perfect for counting. Use defaultdict(int) for simplicity.

Grouping Data

# Group people by age group
people = [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 30},
    {"name": "Charlie", "age": 35}
]

groups = {}
for person in people:
    age_group = "young" if person["age"] < 30 else "old"
    groups.setdefault(age_group, []).append(person["name"])

print(groups)

Grouping is a common dictionary use case.

Best Practices

  1. Use descriptive key names
  2. Prefer get() over direct access for optional keys
  3. Use dict comprehensions for simple transformations
  4. Consider defaultdict for default values
  5. Use tuples as keys when needed (immutable)

External Resources:

Related Tutorials:

Last updated on