Skip to main content

Dictionaries Part 1: Key-Value Mappings

Imagine you're tracking information about students in a class. You could use a list:

Loading Python environment...

But there's a problem: months later, do you remember which index holds the age? Is index 1 the age or the major? When you read that code again, it's confusing.

Dictionaries solve this problem by letting you use meaningful names (called keys) instead of mysterious numbers. Keys map to the values you care about. Instead of students[1], you write student["age"]. The code reads like English.

In this lesson, you'll learn how to create dictionaries, access their values safely, and use type hints to make your intent crystal clear to Python tools and other developers.

What Is a Dictionary?

A dictionary is a Python data structure that stores key-value pairs. Each key maps to one value, like a real dictionary where you look up a word (key) to find its definition (value).

Diagram showing Python dictionary structure with key-value pairs, hash table concept, unique keys requirement, and value access patterns

Here's a simple example:

Loading Python environment...

In this dictionary:

  • Keys are: "name", "age", "major"
  • Values are: "Alice", 20, "Computer Science"
  • The dict[str, str | int] type hint says: "Keys are strings, values can be either strings OR integers"

Key insight: Dictionaries are unordered mappings, not sequences like lists. You don't access values by position (like student[0])—you access them by meaningful keys (like student["age"]).

💬 AI Colearning Prompt

"Why would you use a dictionary instead of a list to store student information? What problem does a key-value structure solve?"

This question helps you think about the intent behind choosing a dictionary. You're not memorizing syntax—you're understanding when to use each structure.

Creating Dictionaries

There are two main ways to create dictionaries: literals (most common) and the dict() constructor (less common, useful for specific patterns).

Dictionary Literals

The most Pythonic way is to use curly braces {} with key: value pairs:

Loading Python environment...

Notice the type hints: Each one tells you what types keys and values expect.

🎓 Expert Insight

In AI-native development, type hints like dict[str, int] aren't just syntax—they're communication. You're telling Python tools, your teammates, and AI collaborators: "Keys are strings, values are integers." This clarity unlocks better suggestions from AI and catches mismatches early.

Union Types for Mixed Values

Sometimes dictionary values have different types. Use the union operator | to express this:

Loading Python environment...

Why use union types? They document intent: "This dict can have multiple value types, and that's intentional."

🚀 CoLearning Challenge

Ask your AI Co-Teacher:

"Create a user profile dictionary with the following fields: username (string), age (integer), is_verified (boolean), email (string). Use proper type hints including union types. Then explain why we use dict[str, str | int | bool] instead of just dict."

Expected Outcome: You'll understand how union types document mixed-type data and why that clarity matters in real code.

The dict() Constructor

Less common, but useful when converting from other data:

Loading Python environment...

For now, stick with literals ({}). The dict() constructor is useful in advanced scenarios.

Accessing Dictionary Values

Now you have a dictionary. How do you get values out?

Bracket Notation: Direct Access

The simplest way—use square brackets with the key:

Loading Python environment...

This works perfectly when you know the key exists. But what if you try to access a key that doesn't exist?

KeyError: The Error You'll See

Access a non-existent key, and you get a KeyError:

Loading Python environment...

The error message is clear: the key "gpa" doesn't exist. This is by design—Python tells you immediately when you ask for something that isn't there.

✨ Teaching Tip

Use Claude Code to experiment with KeyError. Ask: "What does the KeyError traceback tell me? Why is Python strict about missing keys?"

Understanding errors is part of learning. Python's strictness prevents silent bugs.

Safe Access with .get() Method

Often, you don't know if a key exists, and you want a default value if it's missing. Use the .get() method:

Loading Python environment...

Syntax: dict.get(key, default_value)

When to use .get():

  • You're not sure if a key exists
  • You want a sensible default if it's missing
  • You want to avoid KeyError

When to use bracket notation (dict[key]):

  • You're certain the key exists
  • You want an error if the key is missing (catching bugs)

💬 AI Colearning Prompt

"I'm building a feature that retrieves user settings. Sometimes a setting doesn't exist. Should I use bracket notation or .get()? What are the tradeoffs?"

This question teaches you to think about edge cases—what happens when data is incomplete. The answer shapes how you design your code.

Important: Unique Keys

Every key in a dictionary must be unique. If you add a key that already exists, it overwrites the previous value:

Loading Python environment...

This is intentional. If you try to add a duplicate key, Python silently overwrites. This is often what you want (updating a setting), but it's worth knowing.

Practical Example: Student Record System

Let's build a real-world example—a system to track student records:

Loading Python environment...

Output:

Name: Alice
ID: 12345
GPA: Not recorded
GPA (updated): 3.8
Age (updated): 21

What's happening:

  1. We create a student record with mixed types (strings and integers)
  2. We access values safely, knowing which keys exist
  3. We use .get() with a default for optional fields
  4. We update values by adding new keys or reassigning existing ones

This is how real programs track data—with dictionaries, not mysterious lists.

🎓 Expert Insight

Notice we used f-strings for output: f"Name: {student['name']}". This is readable Python. The dictionary structure (meaningful keys) makes the code self-documenting. When you read this code in 6 months, you'll instantly understand what's stored and why.

Preview: Dictionary Methods

Dictionaries have several useful methods. You'll explore these deeply in Lesson 8, but here's a quick preview:

  • .keys() — List all keys: student.keys()
  • .values() — List all values: student.values()
  • .items() — List all key-value pairs: student.items()

Loading Python environment...

These methods are useful when iterating (Lesson 9) or examining dictionary structure. For now, know they exist.

Quick Reference: Bracket vs .get()

ScenarioUse ThisWhy
Key definitely existsdict[key]Direct access, fails loudly if wrong
Key might not exist.get(key, default)Safe fallback, no error
You want an error if missingdict[key]Catches bugs early
You want a sensible default.get(key, default)Handles missing data gracefully

Practice Exercises

Exercise 1: Create a Typed Dictionary

Create a dictionary representing a book with the following fields:

  • title (string): "The Pragmatic Programmer"
  • author (string): "David Thomas"
  • year (integer): 1999
  • pages (integer): 352

Write the dictionary with proper type hints. Then print the title and author.

Loading Python environment...

Check your understanding: Can you explain what dict[str, str | int] means?

Exercise 2: Safe Access with .get()

Using the book dictionary from Exercise 1, safely access:

  • publisher (doesn't exist) with default "Unknown Publisher"
  • pages (exists) to get the actual value
  • rating (doesn't exist) with default 0.0

Print all three values.

Loading Python environment...

Check your understanding: Why does .get() not raise a KeyError?

Exercise 3: Union Types with Mixed Values

Create a dictionary representing a website configuration:

  • domain (string): "example.com"
  • port (integer): 8080
  • ssl_enabled (boolean): True
  • cache_ttl (integer): 3600

Use the correct type hint for mixed types. Then access and print each value.

Loading Python environment...

Check your understanding: Why do we use str | int | bool instead of just str?

Exercise 4: Real-World Application

You're building a contact management system. Create a dictionary for a contact with:

  • name: "Alice Johnson"
  • email: "[email protected]"
  • phone: "555-1234"
  • age: 28
  • verified: True (boolean)

Access the contact's name and email. Then safely retrieve notes (which doesn't exist) with a default value of "No notes".

Loading Python environment...

Check your understanding: What would happen if you tried contact["notes"] with bracket notation? How is .get() different?

Try With AI

Master dictionary access patterns and understand when to use bracket notation vs .get().

🔍 Explore Access Patterns:

"Show me dict['key'] vs dict.get('key', default) with examples. Explain when KeyError is GOOD (required data, fail-fast) vs when defaults are GOOD (optional data). Give real scenarios for each."

🎯 Practice Safe Access:

"Help me create a user profile dict with name, email, age, premium. Access email with brackets, phone (missing) with .get('phone', 'Not provided'). Show what happens with user['phone']."

🧪 Test Type Hint Limits:

"Debug this: user: dict[str, str|int|bool] = {'name': 'Alice'}; user['scores'] = [95, 88]. Does Python stop me? What do type hints actually do? Would mypy catch this? Show TypedDict for strict typing."

🚀 Apply to Config System:

"Build app config with required fields (app_name, port, debug) using brackets, and optional fields (log_file='app.log', max_connections=100) using .get(). Explain why required SHOULD crash if missing."