Skip to main content

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)}")