Advanced Dictionary Operations

Daniel Sarney
Python Intermediate

You already know the basics of dictionaries from the beginner course. Now it's time to unlock their full potential. Dictionaries are one of Python's most powerful data structures, and mastering advanced techniques will make you a more effective programmer.

In this lesson, you'll learn dictionary comprehensions for creating dictionaries efficiently, explore advanced methods like get() and setdefault(), work with nested dictionaries for complex data, and discover how to merge dictionaries elegantly. These skills are essential for working with real-world data, APIs, and configuration management.

What You'll Learn

  • Dictionary comprehensions
  • Dictionary methods (get, setdefault, update, pop)
  • Nested dictionaries
  • Merging dictionaries
  • Defaultdict and Counter from collections
  • Best practices for dictionary usage

Dictionary Comprehensions

Just like list comprehensions, you can create dictionaries concisely using dictionary comprehensions:

# Create a dictionary mapping numbers to their squares
squares = {x: x ** 2 for x in range(1, 6)}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# Create a dictionary from two lists
keys = ["name", "age", "city"]
values = ["Alice", 25, "London"]
person = {k: v for k, v in zip(keys, values)}
print(person)  # {'name': 'Alice', 'age': 25, 'city': 'London'}

# Transform existing dictionary
original = {"apple": 1.20, "banana": 0.80, "orange": 1.50}
# Apply 10% discount
discounted = {fruit: price * 0.9 for fruit, price in original.items()}
print(discounted)  # {'apple': 1.08, 'banana': 0.72, 'orange': 1.35}

You can also add conditions to dictionary comprehensions:

# Only include items with values greater than 1.0
prices = {"apple": 1.20, "banana": 0.80, "orange": 1.50, "grape": 0.60}
expensive = {fruit: price for fruit, price in prices.items() if price > 1.0}
print(expensive)  # {'apple': 1.20, 'orange': 1.50}

# Create dictionary with conditional values
numbers = [1, 2, 3, 4, 5]
parity = {num: "even" if num % 2 == 0 else "odd" for num in numbers}
print(parity)  # {1: 'odd', 2: 'even', 3: 'odd', 4: 'even', 5: 'odd'}

Essential Dictionary Methods

Python dictionaries have several useful methods that make working with them safer and more convenient:

# The get() method - safe key access
person = {"name": "Alice", "age": 25}

# Traditional way (raises KeyError if key doesn't exist)
# city = person["city"]  # Would cause error

# Safe way with get()
city = person.get("city")  # Returns None if key doesn't exist
print(city)  # None

# Provide default value
city = person.get("city", "Unknown")
print(city)  # "Unknown"

# The setdefault() method - set if missing, return value
counts = {}
for word in ["apple", "banana", "apple", "orange", "banana", "apple"]:
    counts.setdefault(word, 0)
    counts[word] += 1
print(counts)  # {'apple': 3, 'banana': 2, 'orange': 1}

# More concise with setdefault
counts = {}
for word in ["apple", "banana", "apple", "orange", "banana", "apple"]:
    counts[word] = counts.setdefault(word, 0) + 1
print(counts)  # {'apple': 3, 'banana': 2, 'orange': 1}

The update() method merges dictionaries, and pop() safely removes items:

# Update method - merge dictionaries
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
dict1.update(dict2)
print(dict1)  # {'a': 1, 'b': 3, 'c': 4} - note 'b' was overwritten

# Pop method - remove and return value
person = {"name": "Alice", "age": 25, "city": "London"}
age = person.pop("age")
print(age)  # 25
print(person)  # {'name': 'Alice', 'city': 'London'}

# Pop with default (no error if key missing)
city = person.pop("country", "Unknown")
print(city)  # "Unknown"

Nested Dictionaries

Dictionaries can contain other dictionaries, creating nested structures perfect for complex data:

# Nested dictionary structure
students = {
    "alice": {
        "age": 20,
        "grades": [85, 90, 88],
        "city": "London"
    },
    "bob": {
        "age": 21,
        "grades": [78, 82, 80],
        "city": "Manchester"
    }
}

# Accessing nested values
alice_age = students["alice"]["age"]
print(alice_age)  # 20

# Safely accessing nested values
charlie_grades = students.get("charlie", {}).get("grades", [])
print(charlie_grades)  # []

# Modifying nested dictionaries
students["alice"]["grades"].append(92)
print(students["alice"]["grades"])  # [85, 90, 88, 92]

# Adding new nested entry
students["charlie"] = {
    "age": 19,
    "grades": [90, 95, 93],
    "city": "Birmingham"
}

Merging Dictionaries

Python 3.9+ introduced the | operator for merging dictionaries:

# Dictionary merging (Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = dict1 | dict2
print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# In-place merge
dict1 |= dict2
print(dict1)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# For older Python versions, use update() or ** unpacking
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Using defaultdict

The defaultdict from the collections module automatically creates default values for missing keys:

from collections import defaultdict

# Regular dictionary - need to check if key exists
word_counts = {}
for word in ["apple", "banana", "apple", "orange"]:
    if word not in word_counts:
        word_counts[word] = 0
    word_counts[word] += 1

# defaultdict - automatically creates default
word_counts = defaultdict(int)  # int() returns 0
for word in ["apple", "banana", "apple", "orange"]:
    word_counts[word] += 1
print(dict(word_counts))  # {'apple': 2, 'banana': 1, 'orange': 1}

# defaultdict with list as default
groups = defaultdict(list)
groups["fruits"].append("apple")
groups["fruits"].append("banana")
groups["vegetables"].append("carrot")
print(dict(groups))
# {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']}

Using Counter

The Counter class makes counting items incredibly easy:

from collections import Counter

# Count items in a list
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
word_counts = Counter(words)
print(word_counts)  # Counter({'apple': 3, 'banana': 2, 'orange': 1})

# Most common items
print(word_counts.most_common(2))  # [('apple', 3), ('banana', 2)]

# Count characters in a string
text = "hello world"
char_counts = Counter(text)
print(char_counts)  # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})

# Counter operations
counter1 = Counter(["a", "b", "c", "a"])
counter2 = Counter(["a", "b", "b"])
print(counter1 + counter2)  # Counter({'a': 3, 'b': 3, 'c': 1})
print(counter1 - counter2)  # Counter({'a': 1, 'c': 1})

Practical Examples

Here are real-world examples combining these techniques:

# Example 1: Configuration management
config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "name": "mydb"
    },
    "api": {
        "key": "secret123",
        "timeout": 30
    }
}

# Safely get nested config with defaults
db_port = config.get("database", {}).get("port", 3306)
print(db_port)  # 5432

# Example 2: Data aggregation
sales = [
    {"product": "apple", "amount": 100},
    {"product": "banana", "amount": 150},
    {"product": "apple", "amount": 200},
    {"product": "orange", "amount": 75}
]

# Aggregate sales by product
from collections import defaultdict
totals = defaultdict(int)
for sale in sales:
    totals[sale["product"]] += sale["amount"]
print(dict(totals))  # {'apple': 300, 'banana': 150, 'orange': 75}

# Example 3: Grouping data
students = [
    {"name": "Alice", "grade": "A", "subject": "Math"},
    {"name": "Bob", "grade": "B", "subject": "Math"},
    {"name": "Charlie", "grade": "A", "subject": "Science"},
    {"name": "David", "grade": "A", "subject": "Math"}
]

# Group by subject
from collections import defaultdict
by_subject = defaultdict(list)
for student in students:
    by_subject[student["subject"]].append(student)
print(dict(by_subject))

Try It Yourself

Practice these exercises:

  1. Word Frequency: Use a Counter to find the most common words in a list of sentences.

  2. Nested Configuration: Create a nested dictionary for application settings (database, logging, features) and safely access values with defaults.

  3. Data Aggregation: Given a list of transactions with "category" and "amount", create a dictionary that sums amounts by category using defaultdict.

  4. Dictionary Comprehension: Create a dictionary that maps numbers 1-10 to their factorial values using a dictionary comprehension.

  5. Merging Configs: Create two configuration dictionaries (default and user) and merge them, with user config overriding defaults.

Summary

You've now mastered advanced dictionary operations that will make you a more effective Python programmer. Dictionary comprehensions let you create dictionaries concisely, while methods like get() and setdefault() make your code safer. Nested dictionaries handle complex data structures, and tools like defaultdict and Counter simplify common tasks.

These techniques are essential for working with real-world data, APIs, configuration files, and any situation where you need to organize and access structured information. As you continue learning, you'll find dictionaries are one of Python's most versatile tools.

What's Next?

In the next lesson, we'll explore the collections module in depth. You'll learn about deque for efficient queues, namedtuple for structured data, and other specialized data structures that solve specific problems elegantly.

Video Tutorial Coming Soon

The video tutorial for this lesson will be available soon. Check back later!

Continue Learning

12 lessons
Python For Beginners

Start your learning journey with Python For Beginners. This course includes comprehensive lessons covering everything you need to know.