16 Dictionaries - Organizing Data with Key-Value Pairs
16.1 Chapter Outline
- Understanding dictionary data structure
- Creating and accessing dictionaries
- Modifying dictionary content
- Dictionary methods and operations
- Iterating through dictionaries
- Nested dictionaries
- Dictionary applications
16.2 Learning Objectives
By the end of this chapter, you will be able to: - Create and initialize Python dictionaries - Access, add, and modify dictionary values - Remove elements from dictionaries using various methods - Iterate through dictionary keys and values - Sort dictionaries by keys or values - Apply dictionaries to solve real-world problems - Use dictionaries to organize complex data
16.3 1. Introduction: The Power of Key-Value Pairs
Dictionaries are one of Python’s most versatile and powerful data structures. Unlike lists, which store items in a specific order accessible by index, dictionaries store data in key-value pairs, allowing you to access values based on meaningful keys rather than numerical positions.
Think of a Python dictionary like a real-world dictionary, where you look up the definition (value) of a word (key). Just as each word in a dictionary has a unique definition, each key in a Python dictionary must be unique.
Dictionaries are perfect for: - Storing related pieces of information - Creating lookup tables - Counting occurrences of items - Representing real-world objects with attributes - Managing configuration settings - Building simple databases
AI Tip: Ask your AI assistant to suggest dictionary applications specific to your field of interest or to explain how dictionaries compare to similar data structures in other programming languages.
16.4 2. Creating and Initializing Dictionaries
There are several ways to create dictionaries in Python:
# Empty dictionary
empty_dict = {}
# Dictionary with initial values
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23}
# Using the dict() constructor
contact = dict(name='Alice', phone='555-1234', email='alice@example.com')
# Creating from two lists (zip creates pairs from two sequences)
keys = ['apple', 'banana', 'cherry']
values = [1.99, 0.99, 2.49]
fruit_prices = dict(zip(keys, values))A few important points to remember: - Dictionary keys must be immutable (strings, numbers, or tuples, not lists) - Values can be any type (numbers, strings, lists, other dictionaries, etc.) - Keys are case-sensitive ('name' and 'Name' are different keys) - Dictionaries are unordered in Python versions before 3.7 (ordered since 3.7)
16.5 3. Accessing Dictionary Elements
You can access dictionary values using their keys in square brackets or with the get() method:
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23}
# Using square brackets
print(student['Name']) # Output: Michael
# Using get() method
print(student.get('Age')) # Output: 23The key difference between these methods is how they handle missing keys:
# Using square brackets with a non-existent key
# print(student['Height']) # Raises KeyError
# Using get() with a non-existent key
print(student.get('Height')) # Output: None
# Using get() with a default value
print(student.get('Height', 'Not specified')) # Output: Not specifiedThe get() method is often preferred for accessing dictionary values because it provides a safer way to handle missing keys without causing errors.
16.6 4. Modifying Dictionary Content
Dictionaries are mutable, meaning you can change, add, or remove their key-value pairs after creation.
16.6.1 Adding or Updating Elements
You can add new key-value pairs or update existing values:
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23}
# Adding a new key-value pair
student['Height'] = 5.8
print(student) # Output: {'Name': 'Michael', 'Sex': 'Male', 'Age': 23, 'Height': 5.8}
# Updating an existing value
student['Age'] = 24
print(student) # Output: {'Name': 'Michael', 'Sex': 'Male', 'Age': 24, 'Height': 5.8}16.6.2 Removing Elements
Python provides multiple ways to remove elements from dictionaries:
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23, 'Height': 5.8, 'Occupation': 'Student'}
# del - Remove a specific key-value pair
del student['Name']
print(student) # {'Sex': 'Male', 'Age': 23, 'Height': 5.8, 'Occupation': 'Student'}
# pop() - Remove a specific key and return its value
sex = student.pop('Sex')
print(sex) # Output: Male
print(student) # {'Age': 23, 'Height': 5.8, 'Occupation': 'Student'}
# popitem() - Remove the last inserted key-value pair
item = student.popitem() # In Python 3.7+, removes the last item
print(item) # Output: ('Occupation', 'Student')
print(student) # {'Age': 23, 'Height': 5.8}
# clear() - Remove all key-value pairs
student.clear()
print(student) # Output: {}Each removal method has its specific use: - del - When you just want to remove a key - pop() - When you want to remove a key and use its value - popitem() - When you want to process items one by one - clear() - When you want to empty the entire dictionary
16.7 5. Dictionary Methods and Operations
Dictionaries come with a rich set of built-in methods that make them even more powerful:
16.7.1 Getting Dictionary Information
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23, 'Height': 5.8, 'Occupation': 'Student'}
# Get all keys
print(student.keys()) # Output: dict_keys(['Name', 'Sex', 'Age', 'Height', 'Occupation'])
# Get all values
print(student.values()) # Output: dict_values(['Michael', 'Male', 23, 5.8, 'Student'])
# Get all key-value pairs as tuples
print(student.items()) # Output: dict_items([('Name', 'Michael'), ('Sex', 'Male'), ...])
# Get the number of key-value pairs
print(len(student)) # Output: 516.7.2 Copying Dictionaries
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23}
# Shallow copy (creates a new dictionary with references to the same values)
student_copy = student.copy()
# Alternative way to create a shallow copy
student_copy2 = dict(student)Note that these methods create shallow copies. For nested dictionaries, you might need a deep copy.
16.8 6. Iterating Through Dictionaries
There are several ways to loop through dictionaries:
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23, 'Height': 5.8, 'Occupation': 'Student'}
# Iterate through keys (default)
for key in student:
print(key) # Output: Name, Sex, Age, Height, Occupation
# Iterate through keys explicitly
for key in student.keys():
print(key) # Output: Name, Sex, Age, Height, Occupation
# Iterate through values
for value in student.values():
print(value) # Output: Michael, Male, 23, 5.8, Student
# Iterate through key-value pairs
for key, value in student.items():
print(f"{key}: {value}")16.8.1 Sorting Dictionaries
Dictionaries themselves are not sortable, but you can sort their keys or items:
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23, 'Height': 5.8, 'Occupation': 'Student'}
# Get sorted keys
sorted_keys = sorted(student.keys())
print(sorted_keys) # Output: ['Age', 'Height', 'Name', 'Occupation', 'Sex']
# Get sorted keys in reverse order
sorted_keys_reverse = sorted(student.keys(), reverse=True)
print(sorted_keys_reverse) # Output: ['Sex', 'Occupation', 'Name', 'Height', 'Age']
# Iterate through dictionary in sorted order
for key in sorted(student.keys()):
print(f"{key}: {student[key]}")16.9 7. Dictionary Comprehensions
Just like list comprehensions, Python offers dictionary comprehensions for creating dictionaries concisely:
# Create a dictionary of squares
squares = {x: x**2 for x in range(1, 6)}
print(squares) # Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Filter items with a condition
even_squares = {x: x**2 for x in range(1, 11) if x % 2 == 0}
print(even_squares) # Output: {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}
# Transform an existing dictionary
student = {'Name': 'Michael', 'Sex': 'Male', 'Age': 23}
uppercase_dict = {k.upper(): v for k, v in student.items()}
print(uppercase_dict) # Output: {'NAME': 'Michael', 'SEX': 'Male', 'AGE': 23}16.10 8. Nested Dictionaries
Dictionaries can contain other dictionaries as values, allowing you to represent complex hierarchical data:
# A dictionary of students
students = {
'S001': {'name': 'Alice', 'age': 20, 'grades': {'math': 85, 'science': 90}},
'S002': {'name': 'Bob', 'age': 21, 'grades': {'math': 92, 'science': 88}},
'S003': {'name': 'Charlie', 'age': 19, 'grades': {'math': 78, 'science': 85}}
}
# Accessing nested values
print(students['S001']['name']) # Output: Alice
print(students['S002']['grades']['math']) # Output: 92
# Modifying nested values
students['S003']['grades']['science'] = 87Nested dictionaries are extremely useful for representing real-world hierarchical data like organizational structures, product catalogs, or student records.
16.11 9. Self-Assessment Quiz
What will be the output of the following code?
d = {'a': 1, 'b': 2} print(d.get('c', 'Not found'))- KeyError: ‘c’
- None
- ‘Not found’
- False
Which method would you use to remove a key-value pair from a dictionary and return the value?
remove()delete()pop()discard()
What happens if you try to access a key that doesn’t exist in a dictionary using square bracket notation (
dict[key])?- It returns None
- It returns a default value
- It raises a KeyError
- It adds the key with a None value
Which of the following is NOT a valid dictionary key type?
- Integer
- String
- List
- Tuple
What will the following code print?
d = {'a': 1, 'b': 2, 'c': 3} for key in sorted(d): print(key, end=' ')- a b c
- 1 2 3
- a 1 b 2 c 3
- The code will raise an error
Answers & Feedback: 1. c) ‘Not found’ — The get() method returns the specified default value when the key is not found 2. c) pop() — This removes the key and returns its value 3. c) It raises a KeyError — Unlike get(), direct access requires the key to exist 4. c) List — Lists are mutable, so they can’t be dictionary keys 5. a) a b c — This code sorts the keys alphabetically and prints them
16.12 10. Common Dictionary Pitfalls
- KeyError: Trying to access a non-existent key without using
get() - Mutating while iterating: Modifying a dictionary while looping through it can lead to unexpected behavior
- Confusing keys and values: Remember that
keys()gives you keys, not values - Shallow vs. deep copying: Be careful with nested dictionaries, as shallow copies don’t copy nested structures
- Dictionary equality: Two dictionaries are equal if they have the same key-value pairs, regardless of order
16.13 Project Corner: Upgrading Your Chatbot with Dictionaries
Let’s enhance our chatbot by using dictionaries to organize response patterns and templates:
import random
# Using dictionaries for more sophisticated response patterns
response_patterns = {
"greetings": ["hello", "hi", "hey", "howdy", "hola", "morning", "evening"],
"farewells": ["bye", "goodbye", "see you", "cya", "farewell", "exit"],
"gratitude": ["thanks", "thank you", "appreciate", "grateful"],
"bot_questions": ["who are you", "what are you", "your name", "your purpose"],
"user_questions": ["how are you", "what's up", "how do you feel"],
"capabilities": ["what can you do", "help", "functions", "abilities", "commands"]
}
response_templates = {
"greetings": [
"Hello there! How can I help you today?",
"Hi! Nice to chat with you!",
"Hey! How's your day going?",
"Greetings! What's on your mind?"
],
"farewells": [
"Goodbye! Come back soon!",
"See you later! Have a great day!",
"Until next time! Take care!",
"Farewell! It was nice chatting with you!"
],
"gratitude": [
"You're welcome!",
"Happy to help!",
"My pleasure!",
"No problem at all!"
],
"bot_questions": [
f"I'm PyBot, a simple chatbot built with Python!",
"I'm a demonstration chatbot for the Python Jumpstart book.",
"I'm your friendly Python-powered conversation partner!"
],
"user_questions": [
"I'm functioning well, thanks for asking!",
"I'm here and ready to chat!",
"I'm operational and at your service!"
],
"capabilities": [
"I can chat about basic topics, remember our conversation, and give responses based on patterns I recognize.",
"Try asking me who I am, say hello, or just chat naturally!",
"I can respond to greetings, questions about myself, and basic conversation. I'm also learning new tricks!"
],
"unknown": [
"I'm not sure I understand. Can you rephrase that?",
"Hmm, I'm still learning and don't quite understand that.",
"That's beyond my current capabilities, but I'm always learning!",
"Interesting, tell me more about that."
]
}
# User stats dictionary to track interaction metrics
user_stats = {
"message_count": 0,
"question_count": 0,
"greeting_count": 0,
"command_count": 0,
"start_time": None,
"topics": {} # Count topics discussed
}
def get_response(user_input):
"""Get a response using dictionary-based pattern matching."""
user_input = user_input.lower().strip()
# Update stats
user_stats["message_count"] += 1
if user_input.endswith("?"):
user_stats["question_count"] += 1
# Check for special commands
if user_input == "stats":
user_stats["command_count"] += 1
return f"""
Conversation Stats:
- Messages sent: {user_stats['message_count']}
- Questions asked: {user_stats['question_count']}
- Greetings: {user_stats['greeting_count']}
- Commands used: {user_stats['command_count']}
- Topics mentioned: {', '.join(user_stats['topics'].keys()) if user_stats['topics'] else 'None'}
""".strip()
# Check for patterns in our response dictionary
for category, patterns in response_patterns.items():
for pattern in patterns:
if pattern in user_input:
# Update stats for this topic/category
if category in user_stats["topics"]:
user_stats["topics"][category] += 1
else:
user_stats["topics"][category] = 1
if category == "greetings":
user_stats["greeting_count"] += 1
# Return a random response from the matching category
return random.choice(response_templates[category])
# No pattern matched, return an unknown response
return random.choice(response_templates["unknown"])
# Main chat loop
bot_name = "PyBot"
print(f"Hello! I'm {bot_name}. Type 'bye' to exit or 'stats' for conversation statistics.")
user_name = input("What's your name? ").strip()
print(f"Nice to meet you, {user_name}!")
from datetime import datetime
user_stats["start_time"] = datetime.now()
conversation_history = []
def save_to_history(speaker, text):
"""Save an utterance to conversation history."""
timestamp = datetime.now().strftime("%H:%M:%S")
conversation_history.append({
"speaker": speaker,
"text": text,
"timestamp": timestamp
})
def show_history():
"""Display the conversation history."""
print("\n----- Conversation History -----")
for entry in conversation_history:
print(f"[{entry['timestamp']}] {entry['speaker']}: {entry['text']}")
print("-------------------------------\n")
# Save initial greeting
save_to_history(bot_name, f"Nice to meet you, {user_name}!")
while True:
user_input = input(f"{user_name}> ")
save_to_history(user_name, user_input)
if user_input.lower() in ["bye", "exit", "quit", "goodbye"]:
duration = datetime.now() - user_stats["start_time"]
minutes = int(duration.total_seconds() // 60)
seconds = int(duration.total_seconds() % 60)
response = f"Goodbye, {user_name}! We chatted for {minutes} minutes and {seconds} seconds."
print(f"{bot_name}> {response}")
save_to_history(bot_name, response)
break
elif user_input.lower() == "history":
show_history()
continue
response = get_response(user_input)
print(f"{bot_name}> {response}")
save_to_history(bot_name, response)Our enhanced chatbot now: 1. Uses dictionaries to organize response patterns and templates 2. Tracks conversation statistics in a dictionary 3. Stores conversation history using dictionaries with timestamps 4. Provides a stats command to view interaction metrics 5. Measures conversation duration 6. Has more diverse response categories
Challenges: - Add a “mood” system that changes response tone based on user interaction - Create a knowledge dictionary where the chatbot can remember facts about the user - Implement a frequency-based suggestion system for common user questions - Allow the user to teach the chatbot new response patterns - Create a persistent settings dictionary that can be saved and loaded
16.14 Cross-References
- Previous Chapter: Strings
- Next Chapter: Files
- Related Topics: Lists (Chapter 11), Looping (Chapter 12)
AI Tip: Ask your AI assistant to suggest ways dictionaries could be used to solve specific data organization problems in your projects.
16.15 Real-World Dictionary Applications
Dictionaries are foundational to many programming tasks. Here are some common real-world applications:
Configuration Settings: Storing application settings in a hierarchical structure.
app_config = { "user": { "name": "Default User", "theme": "dark", "notifications": True }, "system": { "max_threads": 4, "log_level": "info", "debug_mode": False } }Data Transformation: Converting between different data formats.
# Convert user data to API format user = {"first_name": "John", "last_name": "Doe", "age": 30} api_data = { "user": { "name": f"{user['first_name']} {user['last_name']}", "metadata": {"age": user["age"]} } }Caching: Storing computed results for quick access.
# A simple function memoization fibonacci_cache = {} def fibonacci(n): if n in fibonacci_cache: return fibonacci_cache[n] if n <= 1: result = n else: result = fibonacci(n-1) + fibonacci(n-2) fibonacci_cache[n] = result return resultCounting and Statistics: Tracking occurrences of items.
# Count word frequency in a text text = "the quick brown fox jumps over the lazy dog" word_count = {} for word in text.split(): if word in word_count: word_count[word] += 1 else: word_count[word] = 1Lookup Tables: Creating mappings for faster operation.
# Month name to number mapping month_to_num = { "January": 1, "February": 2, "March": 3, "April": 4, "May": 5, "June": 6, "July": 7, "August": 8, "September": 9, "October": 10, "November": 11, "December": 12 }
These examples show why dictionaries are one of Python’s most useful and versatile data structures. As you continue your Python journey, you’ll find countless ways to apply them to make your code more efficient, readable, and powerful.