Python Programming Dictionary
20+ Methods Comprehension

Python Dictionary Complete Guide

Learn all Python dictionary operations - creation, manipulation, built-in methods, comprehension, and advanced operations with practical examples.

Key-Value

Pairs storage

Fast Lookup

O(1) access

Mutable

Can be modified

Unordered

Python 3.7+ ordered

What are Python Dictionaries?

Dictionaries in Python are unordered (Python 3.6 and earlier) or ordered (Python 3.7+) collections of key-value pairs. They are mutable, fast for lookups, and keys must be immutable (strings, numbers, tuples).

Key Concept

Python dictionaries are hash tables that provide O(1) average time complexity for lookups. Keys must be hashable (immutable), while values can be any Python object.

Basic Dictionary Creation and Operations
# Creating dictionaries
empty_dict = {}
person = {"name": "Alice", "age": 30, "city": "New York"}

# Using dict() constructor
person2 = dict(name="Bob", age=25, city="London")
person3 = dict([("name", "Charlie"), ("age", 35), ("city", "Paris")])

# Dictionary with different key types
mixed_keys = {
    "string_key": "value1",
    123: "value2",          # integer key
    (1, 2): "value3",       # tuple key (immutable)
    # [1, 2]: "value4"      # ERROR: list is mutable, not hashable
}

# Accessing values
print(person["name"])       # "Alice" - using key
print(person.get("age"))    # 30 - using get() method
print(person.get("email", "Not found"))  # "Not found" - default value

# Modifying dictionaries
person["age"] = 31          # Update existing key
person["email"] = "alice@example.com"  # Add new key-value pair

# Dictionary length
print(f"Length: {len(person)}")  # 4

# Checking key existence
print("name" in person)     # True
print("phone" in person)    # False
print("phone" not in person) # True

# Deleting items
del person["city"]          # Remove key-value pair
removed_value = person.pop("age")  # Remove and return value
person.popitem()            # Remove and return last item (Python 3.7+)
person.clear()              # Remove all items

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

# Iterate through keys (default)
for key in person:
    print(f"Key: {key}, Value: {person[key]}")

# Iterate through key-value pairs
for key, value in person.items():
    print(f"{key}: {value}")

# Iterate through values
for value in person.values():
    print(f"Value: {value}")

# Iterate through keys
for key in person.keys():
    print(f"Key: {key}")

Python Dictionary Methods Complete Reference

Python provides powerful built-in dictionary methods for various operations. Here's a comprehensive table of all dictionary methods with examples.

Complete Dictionary Methods Reference Table

Category Method Description Syntax Example Result
Access Methods get() Returns value for key, or default if key doesn't exist dict.get("key", "default") value or "default"
Access Methods setdefault() Returns value if key exists, else inserts key with default dict.setdefault("key", "default") value or sets "default"
View Methods keys() Returns view of all keys dict.keys() dict_keys(['key1', 'key2'])
View Methods values() Returns view of all values dict.values() dict_values(['val1', 'val2'])
View Methods items() Returns view of all (key, value) pairs dict.items() dict_items([('key1','val1'),...])
Modification pop() Removes key and returns its value dict.pop("key") removed value
Modification popitem() Removes and returns last (key, value) pair dict.popitem() (key, value)
Modification clear() Removes all items from dictionary dict.clear() {}
Update Methods update() Updates dictionary with key-value pairs from another dict.update(other_dict) merged dictionary
Copy Methods copy() Returns shallow copy of dictionary dict.copy() new dictionary copy
Utility fromkeys() Creates new dictionary with keys from iterable and value dict.fromkeys(keys, value) new dictionary
Important Distinctions:
  • dict[key] vs dict.get(key): bracket notation raises KeyError if key doesn't exist, get() returns None or default
  • dict.keys(), dict.values(), dict.items() return dynamic views that reflect dictionary changes
  • dict.update() merges dictionaries, overwriting existing keys with new values
  • dict.setdefault() is useful for initializing dictionary values
  • dict.copy() creates shallow copy; use copy.deepcopy() for nested dictionaries

Dictionary Methods Practical Examples

See dictionary methods in action with practical examples and real-world use cases.

Dictionary Methods Examples
# get() method - safe access
person = {"name": "Alice", "age": 30}

print(person.get("name"))           # "Alice"
print(person.get("email"))          # None (key doesn't exist)
print(person.get("email", "N/A"))   # "N/A" (with default value)

# Compare with bracket notation
try:
    print(person["email"])          # KeyError!
except KeyError as e:
    print(f"KeyError: {e}")

# setdefault() - get or set with default
person = {"name": "Alice", "age": 30}

# Key exists - returns value
city = person.setdefault("city", "Unknown")
print(f"City: {city}, Person: {person}")  # City: Unknown, Person now has city

# Key exists - returns existing value
name = person.setdefault("name", "Bob")
print(f"Name: {name}")  # Name: Alice (doesn't change existing)

# keys(), values(), items() views
person = {"name": "Alice", "age": 30, "city": "NYC"}

keys_view = person.keys()
values_view = person.values()
items_view = person.items()

print(f"Keys: {keys_view}")    # dict_keys(['name', 'age', 'city'])
print(f"Values: {values_view}") # dict_values(['Alice', 30, 'NYC'])
print(f"Items: {items_view}")  # dict_items([('name', 'Alice'), ...])

# Views are dynamic
person["email"] = "alice@example.com"
print(f"Keys after update: {keys_view}")  # Now includes 'email'

# Convert views to lists
keys_list = list(person.keys())
print(f"Keys as list: {keys_list}")

# pop() - remove and return value
person = {"name": "Alice", "age": 30, "city": "NYC"}
age = person.pop("age")
print(f"Removed age: {age}, Person: {person}")

# pop() with default
email = person.pop("email", "not found")
print(f"Email: {email}")  # "not found" (key didn't exist)

# popitem() - remove and return last item (Python 3.7+)
person = {"name": "Alice", "age": 30, "city": "NYC"}
last_item = person.popitem()
print(f"Removed: {last_item}, Person: {person}")

# clear() - remove all items
person.clear()
print(f"After clear: {person}")  # {}

# update() - merge dictionaries
person = {"name": "Alice", "age": 30}
additional_info = {"city": "NYC", "email": "alice@example.com"}

person.update(additional_info)
print(f"After update: {person}")  # {'name': 'Alice', 'age': 30, 'city': 'NYC', 'email': 'alice@example.com'}

# update() with overwriting
person.update({"age": 31, "city": "Boston"})
print(f"After overwriting update: {person}")  # age and city updated

# update() with multiple formats
person.update([("country", "USA"), ("zip", "10001")])
person.update(state="NY", phone="123-456-7890")
print(f"After multiple updates: {person}")

# copy() - create shallow copy
original = {"name": "Alice", "scores": [85, 90, 78]}
copied = original.copy()

original["name"] = "Bob"
original["scores"].append(95)

print(f"Original: {original}")  # {'name': 'Bob', 'scores': [85, 90, 78, 95]}
print(f"Copied: {copied}")      # {'name': 'Alice', 'scores': [85, 90, 78, 95]} (nested list shared!)

# fromkeys() - create dictionary with default value
keys = ["name", "age", "city"]
default_dict = dict.fromkeys(keys)
print(f"Default dict: {default_dict}")  # {'name': None, 'age': None, 'city': None}

default_dict = dict.fromkeys(keys, "unknown")
print(f"Dict with 'unknown': {default_dict}")  # {'name': 'unknown', 'age': 'unknown', 'city': 'unknown'}

# Dictionary comprehensions
numbers = [1, 2, 3, 4, 5]
squares = {x: x**2 for x in numbers}
print(f"Squares dict: {squares}")  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# Conditional dictionary comprehension
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(f"Even squares: {even_squares}")  # {2: 4, 4: 16}

# Transform existing dictionary
person = {"name": "alice", "age": "30", "city": "nyc"}
uppercase = {k: v.upper() if isinstance(v, str) else v for k, v in person.items()}
print(f"Uppercase values: {uppercase}")  # {'name': 'ALICE', 'age': '30', 'city': 'NYC'}

Dictionary Operations & Techniques

Learn advanced dictionary operations including merging, nesting, and various manipulation techniques.

Dictionary Operations Examples
# Dictionary merging (Python 3.5+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}  # Note: 'b' exists in both

# Method 1: update() (modifies dict1)
dict1.update(dict2)
print(f"After update: {dict1}")  # {'a': 1, 'b': 3, 'c': 4} (dict2's 'b' wins)

# Method 2: {**dict1, **dict2} (creates new dict)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = {**dict1, **dict2}
print(f"Merged: {merged}")  # {'a': 1, 'b': 3, 'c': 4}

# Method 3: | operator (Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1 | dict2
print(f"Merged with |: {merged}")  # {'a': 1, 'b': 3, 'c': 4}

# In-place merge with |= (Python 3.9+)
dict1 |= dict2
print(f"dict1 after |=: {dict1}")  # {'a': 1, 'b': 3, 'c': 4}

# Dictionary comprehension with multiple sources
keys = ["a", "b", "c"]
values = [1, 2, 3]
dict_from_lists = {k: v for k, v in zip(keys, values)}
print(f"Dict from lists: {dict_from_lists}")  # {'a': 1, 'b': 2, 'c': 3}

# Swapping keys and values (if values are hashable)
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(f"Swapped: {swapped}")  # {1: 'a', 2: 'b', 3: 'c'}

# Filtering dictionaries
student_scores = {"Alice": 85, "Bob": 72, "Charlie": 90, "David": 68, "Eve": 95}

# Filter students with score >= 80
top_students = {name: score for name, score in student_scores.items() if score >= 80}
print(f"Top students: {top_students}")  # {'Alice': 85, 'Charlie': 90, 'Eve': 95}

# Sort dictionary by keys
sorted_by_key = dict(sorted(student_scores.items()))
print(f"Sorted by key: {sorted_by_key}")

# Sort dictionary by values
sorted_by_value = dict(sorted(student_scores.items(), key=lambda item: item[1]))
print(f"Sorted by value: {sorted_by_value}")

# Sort by value descending
sorted_desc = dict(sorted(student_scores.items(), key=lambda item: item[1], reverse=True))
print(f"Sorted by value descending: {sorted_desc}")

# Dictionary unpacking
def print_person(name, age, city):
    print(f"{name} is {age} years old and lives in {city}")

person = {"name": "Alice", "age": 30, "city": "NYC"}
print_person(**person)  # Unpacks dictionary as keyword arguments

# Default values with get() for counting
text = "hello world hello python world python python"
word_count = {}

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

print(f"Word count: {word_count}")  # {'hello': 2, 'world': 2, 'python': 3}

# Using defaultdict for counting
from collections import defaultdict

word_count = defaultdict(int)
for word in text.split():
    word_count[word] += 1

print(f"Word count with defaultdict: {dict(word_count)}")

# Grouping with dictionary
people = [
    {"name": "Alice", "city": "NYC"},
    {"name": "Bob", "city": "London"},
    {"name": "Charlie", "city": "NYC"},
    {"name": "David", "city": "Paris"},
    {"name": "Eve", "city": "London"}
]

# Group by city
city_groups = {}
for person in people:
    city = person["city"]
    if city not in city_groups:
        city_groups[city] = []
    city_groups[city].append(person["name"])

print(f"Grouped by city: {city_groups}")
# {'NYC': ['Alice', 'Charlie'], 'London': ['Bob', 'Eve'], 'Paris': ['David']}

# Using setdefault() for grouping
city_groups = {}
for person in people:
    city_groups.setdefault(person["city"], []).append(person["name"])

print(f"Grouped with setdefault: {city_groups}")

# Dictionary as switch/case replacement
def operation_add(a, b):
    return a + b

def operation_subtract(a, b):
    return a - b

def operation_multiply(a, b):
    return a * b

def operation_divide(a, b):
    return a / b if b != 0 else "Cannot divide by zero"

operations = {
    "add": operation_add,
    "subtract": operation_subtract,
    "multiply": operation_multiply,
    "divide": operation_divide
}

# Use like a switch statement
op = "multiply"
result = operations[op](10, 5)
print(f"10 {op} 5 = {result}")  # 10 multiply 5 = 50

Working with Nested Dictionaries

Nested dictionaries (dictionaries within dictionaries) are common in Python for representing complex, hierarchical data structures.

Nested Dictionaries Examples
# Creating nested dictionaries
company = {
    "name": "TechCorp",
    "departments": {
        "engineering": {
            "manager": "Alice",
            "employees": 50,
            "projects": ["Project A", "Project B"]
        },
        "sales": {
            "manager": "Bob",
            "employees": 30,
            "regions": ["North", "South", "East", "West"]
        },
        "hr": {
            "manager": "Charlie",
            "employees": 10
        }
    },
    "location": "Silicon Valley"
}

# Accessing nested values
print(f"Company: {company['name']}")
print(f"Engineering manager: {company['departments']['engineering']['manager']}")
print(f"Sales regions: {company['departments']['sales']['regions']}")

# Safe access with get() in nested dictionaries
print(f"Engineering projects: {company.get('departments', {}).get('engineering', {}).get('projects', 'No projects')}")

# Modifying nested dictionaries
# Add new department
company["departments"]["marketing"] = {
    "manager": "David",
    "employees": 15,
    "budget": 100000
}

# Update existing department
company["departments"]["engineering"]["employees"] = 55
company["departments"]["engineering"]["projects"].append("Project C")

# Iterating through nested dictionaries
print("\nCompany Structure:")
for dept_name, dept_info in company["departments"].items():
    print(f"\nDepartment: {dept_name}")
    for key, value in dept_info.items():
        print(f"  {key}: {value}")

# Flatten nested dictionary
def flatten_dict(d, parent_key='', sep='.'):
    items = []
    for k, v in d.items():
        new_key = f"{parent_key}{sep}{k}" if parent_key else k
        if isinstance(v, dict):
            items.extend(flatten_dict(v, new_key, sep=sep).items())
        else:
            items.append((new_key, v))
    return dict(items)

flat_company = flatten_dict(company)
print(f"\nFlattened company structure: {flat_company}")

# More complex nested structure
university = {
    "name": "State University",
    "colleges": {
        "engineering": {
            "departments": {
                "cs": {
                    "head": "Dr. Smith",
                    "courses": ["CS101", "CS201", "CS301"],
                    "students": 300
                },
                "ee": {
                    "head": "Dr. Johnson",
                    "courses": ["EE101", "EE201"],
                    "students": 200
                }
            },
            "dean": "Dr. Brown"
        },
        "arts": {
            "departments": {
                "english": {
                    "head": "Dr. Williams",
                    "courses": ["ENG101", "ENG201"],
                    "students": 150
                }
            },
            "dean": "Dr. Davis"
        }
    }
}

# Deep access with error handling
try:
    cs_courses = university["colleges"]["engineering"]["departments"]["cs"]["courses"]
    print(f"\nCS Courses: {cs_courses}")
except KeyError as e:
    print(f"Key not found: {e}")

# Using get() with default for deep access
cs_students = university.get("colleges", {}).get("engineering", {}).get("departments", {}).get("cs", {}).get("students", 0)
print(f"CS Students: {cs_students}")

# Recursive function to find all values for a key
def find_all_values(data, target_key):
    results = []
    
    if isinstance(data, dict):
        for key, value in data.items():
            if key == target_key:
                results.append(value)
            elif isinstance(value, (dict, list)):
                results.extend(find_all_values(value, target_key))
    elif isinstance(data, list):
        for item in data:
            results.extend(find_all_values(item, target_key))
    
    return results

all_heads = find_all_values(university, "head")
print(f"All department heads: {all_heads}")

# Dictionary of dictionaries (2D structure)
student_grades = {
    "Alice": {"math": 85, "science": 90, "history": 78},
    "Bob": {"math": 72, "science": 88, "history": 85},
    "Charlie": {"math": 95, "science": 92, "history": 88}
}

# Calculate average for each student
print("\nStudent Averages:")
for student, grades in student_grades.items():
    average = sum(grades.values()) / len(grades)
    print(f"{student}: {average:.2f}")

# Find student with highest math score
best_math = max(student_grades.items(), key=lambda x: x[1]["math"])
print(f"\nBest math score: {best_math[0]} with {best_math[1]['math']}")

# Add new student
student_grades["David"] = {"math": 80, "science": 85, "history": 75}

# Update grade for existing student
student_grades["Alice"]["math"] = 88

# Add new subject for all students
new_subject = "english"
for student in student_grades:
    student_grades[student][new_subject] = 85  # Default grade

print(f"\nUpdated student grades with English: {student_grades}")

Dictionary Comprehension

Dictionary comprehension provides a concise way to create dictionaries. It's similar to list comprehension but produces dictionaries.

Dictionary Comprehension Examples
# Basic dictionary comprehension
# {key_expression: value_expression for item in iterable}

# Create dictionary of squares
numbers = [1, 2, 3, 4, 5]
squares = {x: x**2 for x in numbers}
print(f"Squares: {squares}")  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# With condition
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(f"Even squares: {even_squares}")  # {2: 4, 4: 16}

# Transform existing dictionary
student_scores = {"Alice": 85, "Bob": 72, "Charlie": 90, "David": 68}

# Add bonus points
bonus_scores = {name: score + 5 for name, score in student_scores.items()}
print(f"With bonus: {bonus_scores}")  # {'Alice': 90, 'Bob': 77, 'Charlie': 95, 'David': 73}

# Filter and transform
passing_students = {name: score for name, score in student_scores.items() if score >= 70}
print(f"Passing students: {passing_students}")  # {'Alice': 85, 'Bob': 72, 'Charlie': 90}

# Switch keys and values (if values are unique and hashable)
swapped = {score: name for name, score in student_scores.items()}
print(f"Swapped: {swapped}")  # {85: 'Alice', 72: 'Bob', 90: 'Charlie', 68: 'David'}

# Multiple iterables with zip
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
name_age_dict = {name: age for name, age in zip(names, ages)}
print(f"Name-age dictionary: {name_age_dict}")  # {'Alice': 25, 'Bob': 30, 'Charlie': 35}

# Nested dictionary comprehension
# Create multiplication table
multiplication_table = {i: {j: i*j for j in range(1, 6)} for i in range(1, 6)}
print("\nMultiplication table (1-5):")
for i, row in multiplication_table.items():
    print(f"{i}: {row}")

# Conditional key/value expressions
numbers = [1, 2, 3, 4, 5]
result = {x: "even" if x % 2 == 0 else "odd" for x in numbers}
print(f"Even/odd mapping: {result}")  # {1: 'odd', 2: 'even', 3: 'odd', 4: 'even', 5: 'odd'}

# Flatten nested dictionary using comprehension
nested_dict = {
    "a": {"x": 1, "y": 2},
    "b": {"z": 3},
    "c": {"w": 4, "v": 5}
}

flattened = {f"{outer_key}.{inner_key}": value 
             for outer_key, inner_dict in nested_dict.items() 
             for inner_key, value in inner_dict.items()}
print(f"Flattened: {flattened}")  # {'a.x': 1, 'a.y': 2, 'b.z': 3, 'c.w': 4, 'c.v': 5}

# Dictionary comprehension with enumerate
words = ["apple", "banana", "cherry", "date"]
word_lengths = {word: len(word) for word in words}
print(f"Word lengths: {word_lengths}")  # {'apple': 5, 'banana': 6, 'cherry': 6, 'date': 4}

# With index as key
indexed_words = {i: word for i, word in enumerate(words)}
print(f"Indexed words: {indexed_words}")  # {0: 'apple', 1: 'banana', 2: 'cherry', 3: 'date'}

# Complex transformation
students = [
    {"name": "Alice", "scores": [85, 90, 78]},
    {"name": "Bob", "scores": [72, 88, 85]},
    {"name": "Charlie", "scores": [95, 92, 88]}
]

# Create dictionary of average scores
average_scores = {
    student["name"]: sum(student["scores"]) / len(student["scores"])
    for student in students
}
print(f"Average scores: {average_scores}")  # {'Alice': 84.33, 'Bob': 81.67, 'Charlie': 91.67}

# Dictionary comprehension with if-else in key/value
temperatures = {"NYC": 30, "London": 15, "Mumbai": 35, "Tokyo": 25}
temperature_status = {
    city: ("Hot" if temp > 30 else "Warm" if temp > 20 else "Cool")
    for city, temp in temperatures.items()
}
print(f"Temperature status: {temperature_status}")
# {'NYC': 'Warm', 'London': 'Cool', 'Mumbai': 'Hot', 'Tokyo': 'Warm'}

# Merge multiple dictionaries with comprehension
dicts = [
    {"a": 1, "b": 2},
    {"b": 3, "c": 4},
    {"d": 5}
]

# Last dict wins for duplicate keys
merged = {k: v for d in dicts for k, v in d.items()}
print(f"Merged dictionaries: {merged}")  # {'a': 1, 'b': 3, 'c': 4, 'd': 5}

Dictionary Practice Exercises

Try these exercises to test your understanding of Python dictionaries.

Dictionary Practice Script
# Python Dictionaries Practice Exercises

print("=== Exercise 1: Basic Dictionary Operations ===")
# 1. Create a dictionary of 5 countries and their capitals
capitals = {
    "USA": "Washington D.C.",
    "UK": "London",
    "France": "Paris",
    "Germany": "Berlin",
    "Japan": "Tokyo"
}
print(f"Capitals: {capitals}")

# 2. Add a new country-capital pair
capitals["India"] = "New Delhi"
print(f"After adding India: {capitals}")

# 3. Update an existing capital
capitals["USA"] = "Washington"
print(f"After updating USA: {capitals}")

print("\n=== Exercise 2: Dictionary Manipulation ===")
# 4. Count word frequency in a text
text = "apple banana apple orange banana apple mango"
words = text.split()
word_count = {}
for word in words:
    word_count[word] = word_count.get(word, 0) + 1
print(f"Word frequencies: {word_count}")

# 5. Find the most common word
most_common = max(word_count, key=word_count.get)
print(f"Most common word: '{most_common}' with {word_count[most_common]} occurrences")

print("\n=== Exercise 3: Dictionary Comprehension ===")
# 6. Create dictionary of cubes for numbers 1-10
cubes = {x: x**3 for x in range(1, 11)}
print(f"Cubes 1-10: {cubes}")

# 7. Filter dictionary to include only even cubes
even_cubes = {x: cube for x, cube in cubes.items() if cube % 2 == 0}
print(f"Even cubes: {even_cubes}")

print("\n=== Exercise 4: Nested Dictionaries ===")
# 8. Create student records dictionary
students = {
    1001: {"name": "Alice", "age": 20, "grades": {"math": 85, "science": 90}},
    1002: {"name": "Bob", "age": 21, "grades": {"math": 78, "science": 88}},
    1003: {"name": "Charlie", "age": 19, "grades": {"math": 92, "science": 95}}
}

# Add a new student
students[1004] = {"name": "David", "age": 22, "grades": {"math": 80, "science": 85}}

# Calculate average grade for each student
print("Student averages:")
for student_id, info in students.items():
    grades = info["grades"].values()
    average = sum(grades) / len(grades)
    print(f"{info['name']}: {average:.2f}")

print("\n=== Exercise 5: Advanced Operations ===")
# 9. Merge two dictionaries with custom conflict resolution
dict1 = {"a": 1, "b": 2, "c": 3}
dict2 = {"b": 20, "c": 30, "d": 40}

# Merge with sum for common keys
merged = dict1.copy()
for key, value in dict2.items():
    if key in merged:
        merged[key] += value  # Sum values for common keys
    else:
        merged[key] = value
print(f"Merged with sum for common keys: {merged}")

# 10. Invert dictionary (handle non-unique values)
def invert_dict(d):
    inverted = {}
    for key, value in d.items():
        inverted.setdefault(value, []).append(key)
    return inverted

sample = {"a": 1, "b": 2, "c": 1, "d": 3, "e": 2}
inverted = invert_dict(sample)
print(f"Inverted dictionary: {inverted}")

# 11. Find common keys between two dictionaries
dict_a = {"a": 1, "b": 2, "c": 3, "d": 4}
dict_b = {"b": 20, "c": 30, "e": 50, "f": 60}

common_keys = set(dict_a.keys()) & set(dict_b.keys())
print(f"Common keys: {common_keys}")

# 12. Dictionary difference
keys_only_in_a = set(dict_a.keys()) - set(dict_b.keys())
keys_only_in_b = set(dict_b.keys()) - set(dict_a.keys())
print(f"Keys only in dict_a: {keys_only_in_a}")
print(f"Keys only in dict_b: {keys_only_in_b}")

print("\n=== Exercise 6: Real-world Problems ===")
# 13. Group items by category
items = [
    {"name": "apple", "category": "fruit", "price": 1.0},
    {"name": "banana", "category": "fruit", "price": 0.5},
    {"name": "carrot", "category": "vegetable", "price": 0.8},
    {"name": "broccoli", "category": "vegetable", "price": 1.2},
    {"name": "orange", "category": "fruit", "price": 1.5}
]

# Group by category
category_groups = {}
for item in items:
    category = item["category"]
    category_groups.setdefault(category, []).append(item["name"])

print(f"Items grouped by category: {category_groups}")

# Calculate total price per category
category_totals = {}
for item in items:
    category = item["category"]
    category_totals[category] = category_totals.get(category, 0) + item["price"]

print(f"Total price per category: {category_totals}")

# 14. Find the cheapest item in each category
cheapest_per_category = {}
for item in items:
    category = item["category"]
    if category not in cheapest_per_category or item["price"] < cheapest_per_category[category]["price"]:
        cheapest_per_category[category] = item

print("Cheapest item in each category:")
for category, item in cheapest_per_category.items():
    print(f"{category}: {item['name']} (${item['price']})")

# 15. Create a dictionary from two lists with validation
def create_dict_from_lists(keys, values):
    """Create dictionary from two lists. If lengths differ, use None for missing values."""
    result = {}
    for i, key in enumerate(keys):
        result[key] = values[i] if i < len(values) else None
    return result

keys = ["name", "age", "city", "email"]
values = ["Alice", 30, "NYC"]
result_dict = create_dict_from_lists(keys, values)
print(f"\nDictionary from lists: {result_dict}")

Key Takeaways

  • Python dictionaries are unordered collections of key-value pairs (ordered in Python 3.7+)
  • Keys must be hashable (immutable: strings, numbers, tuples), values can be any Python object
  • Dictionaries provide O(1) average time complexity for lookups, inserts, and deletes
  • Use {} or dict() to create dictionaries
  • Key methods: get(), setdefault(), keys(), values(), items(), pop(), popitem(), update(), copy(), clear()
  • dict[key] raises KeyError for missing keys; dict.get(key, default) returns default
  • Dictionary comprehension: {k: v for item in iterable} creates dictionaries concisely
  • Use in operator to check key existence: 'key' in dictionary
  • dict.keys(), dict.values(), dict.items() return dynamic view objects
  • Dictionaries can be nested to represent complex hierarchical data
  • Merge dictionaries with update(), {**d1, **d2} (Python 3.5+), or | operator (Python 3.9+)
  • Use collections.defaultdict for dictionaries with default values
  • dict.copy() creates shallow copy; use copy.deepcopy() for nested dictionaries