Python is a beginner-friendly language, but as you grow as a programmer, you’ll want to write code that’s not only functional but also clear and maintainable. Two key areas that help with this are typing and type hints and advanced object-oriented programming (OOP) concepts. In this guide, we’ll explore these topics in a way that’s easy for beginners to understand, with plenty of examples. We’ll cover typing (using mypy and pydantic) and advanced OOP topics like abstract classes, multiple inheritance, and magic methods. By the end, you’ll have a solid foundation to write robust, well-structured Python code. Let’s get started!
Part 1: Typing and Type Hints
What Are Typing and Type Hints?
Python is a dynamically typed language, meaning you don’t need to declare the type of a variable (like int or str) when you create it. Python figures it out at runtime. While this is flexible, it can lead to errors in larger projects, as it’s easy to pass the wrong type to a function. Type hints let you add optional type annotations to your code to make it clearer and catch errors early.
For example, instead of:
def add(a, b):
return a + b
You can add type hints:
def add(a: int, b: int) -> int:
return a + b
Here:
-
a: int and b: int indicate that a and b should be integers.
-
-> int indicates the function returns an integer.
Type hints don’t affect how Python runs your code—they’re just hints for developers and tools like mypy. Let’s explore how to use them effectively.
Using Type Hints
Type hints are part of Python’s standard library via the typing module, which provides types like List, Dict, Tuple, and more for complex data structures.
Basic Type Hints
Here’s an example with basic types:
def greet(name: str) -> str:
return f"Hello, {name}!"
age: int = 25
names: list[str] = ["Alice", "Bob"]
print(greet("Alice")) # Output: Hello, Alice!
print(age) # Output: 25
print(names) # Output: ['Alice', 'Bob']
Here:
-
name: str specifies that name is a string.
-
-> str indicates the return value is a string.
-
list[str] indicates a list of strings.
Complex Types with the typing Module
For more complex data structures, use the typing module:
from typing import List, Dict, Tuple
def process_data(scores: List[int], info: Dict[str, int]) -> Tuple[str, int]:
name = list(info.keys())[0]
total = sum(scores)
return (name, total)
scores = [90, 85, 88]
info = {"Alice": 25}
result = process_data(scores, info)
print(result) # Output: ('Alice', 263)
Here:
-
List[int] specifies a list of integers.
-
Dict[str, int] specifies a dictionary with string keys and integer values.
-
Tuple[str, int] specifies a tuple with a string and an integer.
Optional Types and Union
Sometimes a variable can have multiple types. Use Union or Optional:
from typing import Union, Optional
def divide(a: int, b: int) -> Optional[float]:
if b == 0:
return None
return a / b
def describe(value: Union[int, str]) -> str:
return f"Value is {value}"
print(divide(10, 2)) # Output: 5.0
print(divide(10, 0)) # Output: None
print(describe(42)) # Output: Value is 42
print(describe("hello")) # Output: Value is hello
-
Optional[float] means the return value is either a float or None.
-
Union[int, str] means value can be an integer or a string.
Using mypy for Type Checking
mypy is a tool that checks your type hints for errors before running the code. To use it, you need to install it:
pip install mypy
Save this code as example.py:
def add(a: int, b: int) -> int:
return a + b
result = add("hello", 5) # Type error: expects int, got str
Run mypy on the file:
mypy example.py
Output (example):
example.py:4: error: Argument 1 to "add" has incompatible type "str"; expected "int"
Found 1 error in 1 file (checked 1 source file)
mypy catches the error because “hello” is a string, not an integer. Fix it:
result = add(3, 5) # Correct: both arguments are integers
print(result) # Output: 8
mypy helps you catch type-related bugs early, especially in larger projects.
Using pydantic for Data Validation
pydantic is a library that uses type hints for data validation and parsing, making it great for handling user input or APIs. Install it:
pip install pydantic
Here’s an example using pydantic:
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
email: str
# Valid data
user = User(name="Alice", age=25, email="alice@example.com")
print(user) # Output: name='Alice' age=25 email='alice@example.com'
# Invalid data
try:
invalid_user = User(name="Bob", age="thirty", email="bob@example.com")
except ValueError as e:
print(e) # Output: validation error: value is not a valid integer
Here:
-
BaseModel is a pydantic class that uses type hints to validate data.
-
If you pass an invalid type (like “thirty” for age), pydantic raises a ValueError.
pydantic is powerful for validating complex data, like JSON from an API:
from pydantic import BaseModel
from typing import List
class Team(BaseModel):
name: str
members: List[str]
data = {"name": "Dev Team", "members": ["Alice", "Bob"]}
team = Team(**data)
print(team) # Output: name='Dev Team' members=['Alice', 'Bob']
pydantic ensures the data matches the expected types and structure.
Why Use Type Hints?
Type hints make your code:
-
Clearer: Others (and you) can understand what types are expected.
-
Safer: Tools like mypy catch errors before runtime.
-
Better for teams: They act like documentation for large projects.
-
Powerful with libraries: pydantic adds validation for real-world applications.
Start with simple type hints and gradually use mypy and pydantic as your projects grow.
Conclusion
Typing and type hints make your Python code clearer and safer. Use basic type hints for variables and functions, mypy for static checking, and pydantic for data validation. Advanced OOP—with abstract classes, multiple inheritance, and magic methods—lets you build flexible, reusable class hierarchies.
As a beginner, start small:
-
Add type hints to your functions and use mypy to check them.
-
Create an abstract class with one or two methods.
-
Experiment with magic methods like __str__ or __eq__.
-
Try pydantic for validating user input.
Leave a Reply