Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to your first lesson. This course is designed to sharpen your software engineering skills, focusing on the patterns and practices relevant for roles at companies like Anthropic. We'll start by building a strong foundation in writing production-quality Python before moving on to data structures, system design, and more advanced topics.
Today's lesson addresses the first learning outcome: Write self-documenting and type-safe code using Python's typing module and dataclasses.
We will cover:
- The fundamental principles behind Python's type hinting system.
- How to use the
typingmodule to annotate variables, function arguments, and return values. - How to define structured, boilerplate-free data objects using
dataclasses.
Coming from Rust, you are well-acquainted with the benefits of a strong type system. Python's approach, known as "gradual typing," is different in its application but shares the same goals: improving code clarity, enabling powerful tooling, and catching errors before they happen at runtime. This lesson will help you map your existing knowledge to the Python ecosystem.
1. Why Bother with Type Hints?
Before we dive into the syntax, it's important to understand the philosophy. Type hints are not just for documentation or basic error-checking; they are a powerful tool for improving software design. They force you to think more clearly about the data structures and interfaces your code relies on.
To explore this idea further, please watch the first few minutes of the following video.
The REAL Reason You Should Use Type Hints in Python
The video 'The REAL Reason You Should Use Type Hints in Python' from ArjanCodes provides an excellent perspective on how type hints influence code design for the better.
Watch the segment from the beginning until 2:12. Pay close attention to the argument that type hints are primarily a design tool, not just an error-catching mechanism.
As the video argues, thinking about types helps you design more flexible and maintainable components. This principle will be a recurring theme throughout our lessons.
2. Mastering the typing Module
Now, let's get practical. The typing module is the cornerstone of Python's type system. We'll start with the basics and progressively add complexity, mirroring how you might refactor code in a real-world project.
The following video provides a comprehensive tutorial. We'll break it down into sections.
Basic Annotations and Optional Types
First, let's cover the fundamentals: annotating function parameters, return values, and variables. We'll also see how to handle values that can be None, a very common requirement.
Python Tutorial: Type Hints - From Basic Annotations to Advanced Generics
Let's watch the first part of Corey Schafer's 'Python Tutorial: Type Hints'. It gives a clear and practical introduction to adding basic type hints to a Python script.
Watch from the beginning to 08:10. Focus on: The syntax for annotating function arguments and return values. How to express that a parameter is optional (e.g., can be an int or None) using the modern | operator (e.g., int | None). The video also mentions the older Optional syntax, which is good to recognize in existing codebases.
A key takeaway here is the use of | (the union operator) to specify that a variable can be one of several types. The form type | None is the modern and preferred way to denote an optional value.
Structuring Data: From dict to TypedDict
Simple type hints are great, but what about complex data, like dictionaries with a known structure? You could use dict[str, str | int | None], but that's not very specific. It doesn't tell you which keys are expected or what the type of each specific key's value is.
This is where TypedDict comes in. It allows you to define the "shape" of a dictionary, specifying the type for each key. Given your work on infrastructure services, where you likely handle structured data from APIs or configuration files (often in JSON/dict format), TypedDict is a particularly relevant tool.
Let's continue with the video to see how to improve our type definitions.
Python Tutorial: Type Hints - From Basic Annotations to Advanced Generics
This next segment shows the progression from a generic dictionary type hint to a more precise TypedDict, which is ideal for describing the structure of dictionary-based data.
Watch from 09:31 to 12:01, and then from 18:27 to 20:40. Pay attention to: How type aliases can be used to give a clean name to a complex type. How TypedDict solves the problem of specifying types for individual keys within a dictionary. The way a type checker like mypy can now catch more specific errors once you use TypedDict.
3. dataclasses: The Modern Way to Hold Data
While TypedDict is excellent for describing dictionaries, you often want to work with actual objects in your application logic—objects that might have methods and other behaviors. Before Python 3.7, this meant writing a lot of boilerplate code for a class: an __init__ method to assign attributes, a __repr__ method for clean printing, an __eq__ method for comparisons, and so on.
The @dataclass decorator automates all of this, letting you define a class with typed attributes concisely.

From TypedDict to dataclass
A dataclass is often the next logical step after a TypedDict when you realize you need more than just a dictionary. Let's see how this transition looks.
Python Tutorial: Type Hints - From Basic Annotations to Advanced Generics
Continuing with Corey Schafer's tutorial, this part shows how to convert the User TypedDict into a full-fledged dataclass, highlighting the benefits of a true class-based approach.
Watch from 20:40 to 24:06. Focus on: The minimal changes needed to convert the TypedDict to a dataclass. How a dataclass instance is created versus a dictionary. The fact that a dataclass creates a proper class, which can have methods and other logic, unlike a TypedDict.
Key dataclass Features for Production Code
To write robust code, you need more than just the basics. The dataclasses module provides powerful features that are essential for building reliable systems.
Let's explore some of these in the article "Python Dataclasses: A Complete Guide to Boilerplate-Free Objects".
Python Dataclasses: A Complete Guide to Boilerplate‑Free Objects
This article provides a comprehensive overview of dataclasses. We will focus on a few key features that are particularly useful in production settings.
Read the following sections from the article (about 10 minutes): "Defining Fields and Types": Understand how to set default values for fields. "Default Values and default_factory": Pay close attention to this. Using mutable defaults like [] or {} directly is a common and serious bug in Python. default_factory is the correct solution. "Hashability and frozen=True": This creates immutable dataclasses. This is analogous to Rust's emphasis on immutability and is crucial for creating objects that can be used as dictionary keys or in sets. "Customizing __post_init__": Learn how to add validation or computed fields that run after the object is created.
These features—default_factory, frozen=True, and __post_init__—are hallmarks of professional Python code. Using them correctly demonstrates a deep understanding of how to create safe and predictable data objects.
4. Best Practices: Inputs Generic, Outputs Specific
As you integrate type hints into your code, a powerful guiding principle emerges, which is especially important for designing reusable functions and modules.
The REAL Reason You Should Use Type Hints in Python
Let's return to the ArjanCodes video to understand a core best practice for applying type hints effectively.
Watch the segments from 02:12 to 05:25 and 08:02 to 12:33. Focus on the core message: Inputs should be as generic as possible (e.g., accept an Iterable instead of just a list). Outputs should be as specific as possible (e.g., return a list if you know you're creating a list, not a generic Iterable).\nThis is known as the Liskov Substitution Principle applied to function signatures.
Adhering to this principle makes your code more flexible for callers while providing them with the most precise information possible about what your functions return.
Summary and Key Takeaways
This lesson covered the foundations of writing modern, type-safe Python code.
- Type hints are a design tool that improves code clarity, enables static analysis with tools like
mypy, and enhances IDE support. - The
typingmodule provides the tools to annotate everything from simple types (str,int) to complex structures (list[str],dict[str, int],int | None). TypedDictis perfect for describing the shape of dictionaries, especially when interfacing with external systems like JSON APIs.dataclassesprovide a concise and powerful way to create structured data objects, eliminating boilerplate and offering features like immutability (frozen=True), safe defaults (default_factory), and post-creation validation (__post_init__).- A key design principle is to make your function inputs generic and your outputs specific.
In an interview setting, demonstrating a clear understanding of when to use a TypedDict versus a dataclass, and correctly using features like default_factory, signals a high level of proficiency in Python.
Next Lesson
Now that you know how to define clear, type-safe data structures, our next lesson will focus on the containers you use to store them. We will dive into the practical differences and use cases for standard dicts, collections.OrderedDict, and collections.defaultdict.