Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
311: TypedDict for Typed Dictionaries
You've probably spent plenty of time passing dictionaries around your code. They're the Swiss Army knife of Python—perfect for when you need to bundle some data together without the overhead of defining a full class. But as your project grows, a plain dictionary becomes a liability. When you're looking at a function signature that says data: dict, you have no idea what's actually inside that dictionary without hunting through the code to find where it was instantiated.
The "Anything Goes" Dictionary
Let's look at how we usually handle this when we're moving too fast. Imagine we're building a system to handle user sessions. We might write something like this:
def process_session(session: dict):
# I'm just guessing these keys exist
username = session["username"]
user_id = session["user_id"]
print(f"Processing {username} ({user_id})")
On the surface, this is fine. But here is where it breaks: your IDE has no clue what session contains. You get no autocomplete for the keys, and if you accidentally type session["user_ID"] (uppercase D), Python won't complain until the code actually crashes at runtime with a KeyError. If you try to fix this by using Dict[str, Any], you've essentially just told the type checker, "Trust me, it's a dictionary, but don't ask me what's in it." That's not really helping us.
Adding a Schema with TypedDict
This is where TypedDict comes in. It allows you to define a "schema" for your dictionary. It doesn't change how the dictionary behaves at runtime—it's still just a regular Python dict—but it gives your static analysis tools (like Mypy or Pyright) the blueprint they need to protect you.
from typing import TypedDict
class UserSession(TypedDict):
username: str
user_id: int
is_admin: bool
def process_session(session: UserSession):
# Now my IDE knows exactly what keys are available
username = session["username"]
print(f"Processing {username}")
The difference here is subtle but massive. If I try to assign a string to user_id or access a key called email that isn't in the UserSession definition, my editor will highlight it in red immediately. I don't have to run the code to find the bug.
The Cost of Rigidity
Now, you might wonder why we wouldn't just use a dataclass or a NamedTuple. The trade-off is about intent and compatibility. If you're working with JSON APIs, your data arrives as a dictionary. Converting every single API response into a class instance takes time and adds boilerplate. TypedDict lets you keep the lightweight nature of a dictionary while gaining the safety of a class.
One thing to keep in mind: TypedDict is strictly for type checking. If you pass a dictionary that is missing a required key into process_session, Python will not stop you at runtime. It's a contract for the developer, not a validation layer for the data. If you need the code to actually crash the moment a key is missing during execution, you'll still want to look at something like Pydantic.
📋 Practical Task
Refactoring the Movie Metadata Pipeline
You have a function that processes movie data coming from a legacy JSON API. Currently, it uses a generic dictionary, which is causing bugs because some developers are using "year" while others are using "release_year".
Your Task:
- Define a
TypedDictcalledMovieMetadatathat requires three keys:title(str),release_year(int), andrating(float). - Update the
analyze_moviefunction signature to useMovieMetadatainstead ofdict. - Create a movie dictionary that deliberately misses one of the required keys or uses the wrong type, and observe how your type checker (or IDE) flags the error.
from typing import TypedDict
# TODO: Define MovieMetadata TypedDict here
def analyze_movie(movie: dict): # TODO: Update this type hint
print(f"Analyzing {movie['title']} from {movie['release_year']}")
# TODO: Create a movie instance and pass it to the function
movie_data = {
"title": "Inception",
"release_year": 2010,
"rating": 8.8
}
analyze_movie(movie_data)There are no comments for now.