Dictionaries
Introduction
Dictionaries in Python are collections of key-value pairs. They allow us to group related data and behavior into one unit. Keys are usually strings (but can be any immutable type), and values can be any type — numbers, strings, lists, functions, or even other dictionaries.
Dictionaries are fundamental in Python: they're used for everything from configuration files to database records to caching. Understanding dictionaries is essential for structuring data and building larger programs. Python dictionaries are ordered (as of Python 3.7+) and mutable.
Concepts
Creating Dictionaries
- Dictionary literals:
person = {"name": "Alice", "age": 25} - Using
dict()constructor:person = dict(name="Alice", age=25) - Dictionary comprehensions:
{k: v for k, v in items}
Accessing Values
- Bracket notation:
person["name"] .get()method:person.get("name", "default")(safe access)
Adding & Modifying Values
person["city"] = "London"
person["age"] = 26
Deleting Items
del person["city"]
# or
removed_value = person.pop("city", None)
Dictionary Methods
person.keys() # Get all keys
person.values() # Get all values
person.items() # Get key-value pairs
person.update(other_dict) # Merge dictionaries
Dictionary Comprehensions
squares = {x: x**2 for x in range(5)}
Nested Dictionaries
Dictionaries can contain other dictionaries:
company = {
"name": "TechCorp",
"address": {"city": "London", "country": "UK"}
}
Examples
car = {"make": "Toyota", "year": 2020}
car["color"] = "Blue"
# Dictionary methods
print(car.keys()) # dict_keys(['make', 'year', 'color'])
print(car.values()) # dict_values(['Toyota', 2020, 'Blue'])
# Safe access
model = car.get("model", "Unknown")
# Dictionary comprehension
car_info = {k.upper(): v for k, v in car.items()}
Challenges
1. Create student dictionary
- Create a dictionary student with properties: name, age, and course. Print each property.
💡 Show Answer
student = {"name": "Alice", "age": 21, "course": "Computer Science"}
print(student["name"])
print(student["age"])
print(student["course"])
# Alternative using get()
print(student.get("name"))
print(student.get("age"))
print(student.get("course"))
# Print all using loop
for key, value in student.items():
print(f"{key}: {value}")
2. Add grade & update age
- Add a new property grade to student and update the age.
💡 Show Answer
student["grade"] = "A"
student["age"] = 22
print(student)
# Using update() for multiple changes
student.update({"grade": "A+", "age": 23})
print(student)
# Alternative syntax for update
student.update(grade="B", age=24)
print(student)
3. Delete property
- Delete the student's course property.
💡 Show Answer
# Method 1: del keyword
if "course" in student:
del student["course"]
# Method 2: pop() method (safer)
removed_course = student.pop("course", None)
print(f"Removed: {removed_course}")
# Method 3: popitem() removes and returns last item
# last_item = student.popitem()
print(student)
4. Add method simulation
- Create a book dictionary and simulate a method by storing a function.
💡 Show Answer
def get_summary(book_dict):
return f"The book '{book_dict['title']}' was written by {book_dict['author']}"
book = {
"title": "Harry Potter",
"author": "J.K. Rowling",
"get_summary": lambda: f"The book '{book['title']}' was written by {book['author']}"
}
print(get_summary(book))
print(book["get_summary"]())
# More Pythonic approach using a class (preview of next chapters)
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def get_summary(self):
return f"The book '{self.title}' was written by {self.author}"
book_obj = Book("Harry Potter", "J.K. Rowling")
print(book_obj.get_summary())
5. Dynamic key access
- Use dynamic key access to access properties with special characters or spaces.
💡 Show Answer
person = {
"name": "Alice",
"age": 25,
"favorite color": "blue", # key with a space
"email@address": "alice@example.com" # key with special characters
}
# Accessing with bracket notation
print(person["favorite color"]) # blue
print(person["email@address"]) # alice@example.com
# Dynamic access
key = "favorite color"
print(person[key]) # blue
# Check if key exists before accessing
if "favorite color" in person:
print(f"Favorite color: {person['favorite color']}")
# Using get() for safe access
color = person.get("favorite color", "not specified")
print(f"Color: {color}")
6. Dictionary unpacking
- Create a car dictionary with make and year properties.
- Use dictionary unpacking to extract values into variables.
💡 Show Answer
car = {"make": "Toyota", "year": 2020}
# Method 1: Direct assignment
make = car["make"]
year = car["year"]
# Method 2: Multiple assignment with get()
make, year = car.get("make"), car.get("year")
# Method 3: Using values() (order dependent)
make, year = car.values()
# Method 4: Dictionary unpacking in function calls
def describe_car(make, year):
return f"{year} {make}"
print(describe_car(**car)) # 2020 Toyota
# Method 5: Unpacking with additional values
def full_car_info(make, year, color="Unknown"):
return f"{year} {make}, Color: {color}"
car_with_color = {**car, "color": "Red"}
print(full_car_info(**car_with_color))
7. Nested dictionaries
- Create a library dictionary with a books dictionary containing two book dictionaries.
- Access and print the details of each book.
💡 Show Answer
library = {
"name": "Central Library",
"books": {
"book1": {"title": "1984", "author": "George Orwell"},
"book2": {"title": "To Kill a Mockingbird", "author": "Harper Lee"}
}
}
# Access nested values
print(library["books"]["book1"]["title"]) # 1984
print(library["books"]["book2"]["title"]) # To Kill a Mockingbird
# Loop through nested structure
for book_id, book_info in library["books"].items():
print(f"{book_id}: '{book_info['title']}' by {book_info['author']}")
# Safe nested access using get()
title1 = library.get("books", {}).get("book1", {}).get("title", "Unknown")
print(f"Book 1 title: {title1}")
# More complex nested structure
complex_library = {
"branches": {
"downtown": {
"address": "123 Main St",
"books": [
{"title": "Python Basics", "isbn": "123"},
{"title": "Data Science", "isbn": "456"}
]
},
"uptown": {
"address": "456 Oak Ave",
"books": [
{"title": "Web Development", "isbn": "789"}
]
}
}
}
# Access books from specific branch
for book in complex_library["branches"]["downtown"]["books"]:
print(f"Downtown has: {book['title']}")
8. Count properties
- Write a function count_properties(dictionary) that returns how many key-value pairs a dictionary has.
💡 Show Answer
def count_properties(dictionary):
return len(dictionary)
student = {
"name": "Alice",
"age": 21,
"is_graduate": True,
"courses": ["Math", "Science"]
}
print(count_properties(student)) # 4
# More detailed analysis
def analyze_dictionary(d):
info = {
"total_keys": len(d),
"key_types": {},
"value_types": {}
}
for key, value in d.items():
key_type = type(key).__name__
value_type = type(value).__name__
info["key_types"][key_type] = info["key_types"].get(key_type, 0) + 1
info["value_types"][value_type] = info["value_types"].get(value_type, 0) + 1
return info
analysis = analyze_dictionary(student)
print(f"Dictionary analysis: {analysis}")
# Count nested properties recursively
def count_all_properties(obj, count=0):
if isinstance(obj, dict):
count += len(obj)
for value in obj.values():
count = count_all_properties(value, count)
elif isinstance(obj, list):
for item in obj:
count = count_all_properties(item, count)
return count
nested_dict = {
"level1": {"level2": {"level3": "value"}},
"list": [{"item": "value"}]
}
print(f"Total nested properties: {count_all_properties(nested_dict)}")
9. Loop through keys/values
- Loop over a dictionary's keys and values using different methods.
💡 Show Answer
student = {
"name": "Alice",
"age": 21,
"is_graduate": True,
"gpa": 3.8
}
# Method 1: Loop through keys
print("Keys only:")
for key in student:
print(f"Key: {key}")
# Method 2: Loop through keys explicitly
print("\nKeys (explicit):")
for key in student.keys():
print(f"Key: {key}, Value: {student[key]}")
# Method 3: Loop through values
print("\nValues only:")
for value in student.values():
print(f"Value: {value}")
# Method 4: Loop through items (key-value pairs)
print("\nKey-Value pairs:")
for key, value in student.items():
print(f"{key}: {value}")
# Method 5: Enumerate for index tracking
print("\nWith index:")
for index, (key, value) in enumerate(student.items()):
print(f"{index}: {key} = {value}")
# Method 6: Filtered loops
print("\nString values only:")
for key, value in student.items():
if isinstance(value, str):
print(f"{key}: {value}")
# Method 7: Dictionary comprehension for filtering
string_items = {k: v for k, v in student.items() if isinstance(v, str)}
print(f"\nString items: {string_items}")
10. Merge dictionaries
- Merge two dictionaries (contact_info and personal_info) using different methods.
💡 Show Answer
contact_info = {
"name": "Sean Bean",
"email": "sbean@actorsguild.com",
"telephone": "07354635456"
}
personal_info = {
"name": "Sean Bean", # Duplicate key
"address": "Some Street",
"number": 42,
"postcode": "L346DF",
"county": "Merseyside"
}
# Method 1: Dictionary unpacking (Python 3.5+)
merged_simple = {**contact_info, **personal_info}
print(f"Simple merge: {merged_simple}")
# Note: personal_info's "name" overwrites contact_info's "name"
# Method 2: Using update()
merged_update = contact_info.copy()
merged_update.update(personal_info)
print(f"Update merge: {merged_update}")
# Method 3: Dictionary union (Python 3.9+)
# merged_union = contact_info | personal_info
# Method 4: Preserve both objects structure
merged_structured = {
"contact": contact_info.copy(),
"personal": personal_info.copy()
}
print(f"Structured merge: {merged_structured}")
# Method 5: Custom merge function to handle conflicts
def merge_with_conflict_resolution(dict1, dict2, prefer="dict2"):
"""Merge dictionaries with conflict resolution."""
merged = dict1.copy()
conflicts = {}
for key, value in dict2.items():
if key in merged and merged[key] != value:
conflicts[key] = {"dict1": merged[key], "dict2": value}
merged[key] = value if prefer == "dict2" else merged[key]
else:
merged[key] = value
return merged, conflicts
result, conflicts = merge_with_conflict_resolution(contact_info, personal_info)
print(f"Merge result: {result}")
print(f"Conflicts: {conflicts}")
# Method 6: Deep merge for nested dictionaries
def deep_merge(dict1, dict2):
"""Deep merge two dictionaries."""
result = dict1.copy()
for key, value in dict2.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = value
return result
nested_dict1 = {"user": {"name": "Alice", "settings": {"theme": "dark"}}}
nested_dict2 = {"user": {"age": 25, "settings": {"language": "en"}}}
deep_merged = deep_merge(nested_dict1, nested_dict2)
print(f"Deep merge: {deep_merged}")
# Method 7: Chain multiple dictionaries
from collections import ChainMap
chained = ChainMap(personal_info, contact_info)
print(f"Chained lookup: {chained['name']}") # Gets from first dict with the key
print(f"All chained items: {dict(chained)}")