Python Dictionaries
What is a Dictionary?
A dictionary stores data as key-value pairs.
# Basic structure
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
# Access by key
print(student['name']) # Alex
print(student['age']) # 20
Creating Dictionaries
# Empty dictionary
empty = {}
# With initial values
person = {'name': 'Alex', 'age': 20}
# Using dict() constructor
person = dict(name='Alex', age=20)
Basic Operations
Adding and Modifying
student = {'name': 'Alex', 'age': 20}
# Add new key
student['major'] = 'CS'
print(student) # {'name': 'Alex', 'age': 20, 'major': 'CS'}
# Modify existing value
student['age'] = 21
print(student) # {'name': 'Alex', 'age': 21, 'major': 'CS'}
Deleting
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
# Delete specific key
del student['major']
print(student) # {'name': 'Alex', 'age': 20}
# Remove and return value
age = student.pop('age')
print(age) # 20
print(student) # {'name': 'Alex'}
Getting Values Safely
student = {'name': 'Alex', 'age': 20}
# Direct access - raises error if key missing
print(student['name']) # Alex
# print(student['grade']) # KeyError!
# Safe access with .get() - returns None if missing
print(student.get('name')) # Alex
print(student.get('grade')) # None
# Provide default value
print(student.get('grade', 'N/A')) # N/A
Useful Methods
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
# Get all keys
print(student.keys()) # dict_keys(['name', 'age', 'major'])
# Get all values
print(student.values()) # dict_values(['Alex', 20, 'CS'])
# Get all key-value pairs
print(student.items()) # dict_items([('name', 'Alex'), ('age', 20), ('major', 'CS')])
# Get length
print(len(student)) # 3
Membership Testing
Use in to check if a key exists (not value!):
student = {'name': 'Alex', 'age': 20}
# Check if key exists
print('name' in student) # True
print('grade' in student) # False
# Check if key does NOT exist
print('grade' not in student) # True
# To check values, use .values()
print('Alex' in student.values()) # True
print(20 in student.values()) # True
Important: Checking in on a dictionary is O(1) - instant! This is why dictionaries are so powerful.
Looping Through Dictionaries
Loop Over Keys (Default)
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
# Default: loops over keys
for key in student:
print(key)
# name
# age
# major
# Explicit (same result)
for key in student.keys():
print(key)
Loop Over Values
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
for value in student.values():
print(value)
# Alex
# 20
# CS
Loop Over Keys and Values Together
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
for key, value in student.items():
print(f"{key}: {value}")
# name: Alex
# age: 20
# major: CS
Loop With Index Using enumerate()
student = {'name': 'Alex', 'age': 20, 'major': 'CS'}
for index, key in enumerate(student):
print(f"{index}: {key} = {student[key]}")
# 0: name = Alex
# 1: age = 20
# 2: major = CS
# Or with items()
for index, (key, value) in enumerate(student.items()):
print(f"{index}: {key} = {value}")
Dictionary Order
Python 3.7+: Dictionaries maintain insertion order.
# Items stay in the order you add them
d = {}
d['first'] = 1
d['second'] = 2
d['third'] = 3
for key in d:
print(key)
# first
# second
# third (guaranteed order!)
Note: Before Python 3.7, dictionary order was not guaranteed. If you need to support older Python, don't rely on order.
Important: While keys maintain insertion order, this doesn't mean dictionaries are sorted. They just remember the order you added things.
# Not sorted - just insertion order
d = {'c': 3, 'a': 1, 'b': 2}
print(list(d.keys())) # ['c', 'a', 'b'] - insertion order, not alphabetical
Complex Values
Lists as Values
student = {
'name': 'Alex',
'courses': ['Math', 'Physics', 'CS']
}
# Access list items
print(student['courses'][0]) # Math
# Modify list
student['courses'].append('Biology')
print(student['courses']) # ['Math', 'Physics', 'CS', 'Biology']
Nested Dictionaries
students = {
1: {'name': 'Alex', 'age': 20},
2: {'name': 'Maria', 'age': 22},
3: {'name': 'Jordan', 'age': 21}
}
# Access nested values
print(students[1]['name']) # Alex
print(students[2]['age']) # 22
# Modify nested values
students[3]['age'] = 22
# Add new entry
students[4] = {'name': 'Casey', 'age': 19}
Why Dictionaries Are Fast: Hashing
Dictionaries use hashing to achieve O(1) lookup time.
How it works:
- When you add a key, Python computes a hash (a number) from the key
- This hash tells Python exactly where to store the value in memory
- When you look up the key, Python computes the same hash and goes directly to that location
Result: Looking up a key takes the same time whether your dictionary has 10 items or 10 million items.
# List: O(n) - must check each element
my_list = [2, 7, 11, 15]
if 7 in my_list: # Checks: 2? no. 7? yes! (2 checks)
print("Found")
# Dictionary: O(1) - instant lookup
my_dict = {2: 'a', 7: 'b', 11: 'c', 15: 'd'}
if 7 in my_dict: # Goes directly to location (1 check)
print("Found")
Practical Example: Two Sum Problem
Problem: Find two numbers that add up to a target.
Slow approach (nested loops - O(n²)):
nums = [2, 7, 11, 15]
target = 9
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
print([i, j]) # [0, 1]
Fast approach (dictionary - O(n)):
nums = [2, 7, 11, 15]
target = 9
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
print([seen[complement], i]) # [0, 1]
else:
seen[num] = i
Why it's faster:
- We loop once through the array
- For each number, we check if its complement exists (O(1) lookup)
- Total: O(n) instead of O(n²)
Trace through:
i=0, num=2: complement=7, not in seen, add {2: 0}
i=1, num=7: complement=2, IS in seen at index 0, return [0, 1]
Exercises
Exercise 1: Create a dictionary of 5 countries and their capitals. Print each country and its capital.
Exercise 2: Write a program that counts how many times each character appears in a string.
Exercise 3: Given a list of numbers, create a dictionary where keys are numbers and values are their squares.
Exercise 4: Create a program that stores product names and prices. Let the user look up prices by product name.
Exercise 5: Given a 5×5 list of numbers, count how many times each number appears and print the three most common.
Exercise 6: DNA pattern matching - given a list of DNA sequences and a pattern with wildcards (*), find matching sequences:
sequences = ['ATGCATGC', 'ATGGATGC', 'TTGCATGC']
pattern = 'ATG*ATGC' # * matches any character
# Should match: 'ATGCATGC', 'ATGGATGC'
Solutions
# Exercise 1
capitals = {'France': 'Paris', 'Japan': 'Tokyo', 'Italy': 'Rome',
'Egypt': 'Cairo', 'Brazil': 'Brasilia'}
for country, capital in capitals.items():
print(f"{country}: {capital}")
# Exercise 2
text = "hello world"
char_count = {}
for char in text:
char_count[char] = char_count.get(char, 0) + 1
print(char_count)
# Exercise 3
numbers = [1, 2, 3, 4, 5]
squares = {n: n**2 for n in numbers}
print(squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Exercise 4
products = {}
while True:
name = input("Product name (or 'done'): ")
if name == 'done':
break
price = float(input("Price: "))
products[name] = price
while True:
lookup = input("Look up product (or 'quit'): ")
if lookup == 'quit':
break
print(products.get(lookup, "Product not found"))
# Exercise 5
import random
grid = [[random.randint(1, 10) for _ in range(5)] for _ in range(5)]
counts = {}
for row in grid:
for num in row:
counts[num] = counts.get(num, 0) + 1
# Sort by count and get top 3
top3 = sorted(counts.items(), key=lambda x: x[1], reverse=True)[:3]
print("Top 3:", top3)
# Exercise 6
sequences = ['ATGCATGC', 'ATGGATGC', 'TTGCATGC']
pattern = 'ATG*ATGC'
for seq in sequences:
match = True
for i, char in enumerate(pattern):
if char != '*' and char != seq[i]:
match = False
break
if match:
print(f"Match: {seq}")
Summary
| Operation | Syntax | Time |
|---|---|---|
| Create | d = {'a': 1} | O(1) |
| Access | d['key'] | O(1) |
| Add/Modify | d['key'] = value | O(1) |
| Delete | del d['key'] | O(1) |
| Check key exists | 'key' in d | O(1) |
| Get all keys | d.keys() | O(1) |
| Get all values | d.values() | O(1) |
| Loop | for k in d | O(n) |
Key takeaways:
- Dictionaries are fast for lookups (O(1))
- Use
.get()for safe access with default values - Loop with
.items()to get both keys and values - Python 3.7+ maintains insertion order
- Perfect for counting, caching, and mapping data