website/docs/python-typing-for-beginners.mdx
{/*
import CodeSnippet from '@site/src/sandbox/CodeSnippet'
A beginner‑friendly guide to adding type hints in Python.
Note: This tutorial assumes you understand some basic Python syntax, but are new to programming with type hints. To learn more about Python, see the Python Tutorial and Getting Started Guide
A type is a classification that defines what operations can be performed on a piece of data, what values it can hold, and how it behaves in memory. Types are fundamental to programming because they help ensure that operations on data make sense.
For example:
int (integer) type can be added, subtracted, or multipliedstr (string) type can be concatenated or splitlist type can be indexed, sliced, or iterated overNote: These are just examples of common operations for each data type. Python's built-in types support many more operations that are not listed here.
Understanding types helps you predict how your code will behave and avoid runtime errors from trying to perform operations that don't make sense, such as dividing a string by a number.
A type hint in Python is a way to indicate the expected data type of a variable, function parameter, or return value. It's a hint to other developers (and to tools like type checkers and IDEs) about what type of data should be used with a particular piece of code.
Type hints are not enforced at runtime by Python itself, but they can be used by third-party tools (like Pyrefly) to catch type-related errors before your code runs. They also serve as documentation, making it easier for others to understand how to use your code. Here's an example of a simple function with type hints:
def greet(name: str) -> None:
print(f"Hello, {name}!")
In this example:
name: str indicates that the name parameter should be a string.-> None specifies that the function doesn't return any value (similar to void in other languages).Python is a dynamically typed language, which means you can write code without declaring types. However, this can lead to bugs or ambiguity in your code.
TL;DR
<CodeSnippet sampleFilename="why_hints.py" codeSample={`# Without hints – is "times" a str, int, or list? def repeat(text, times): return text * times
def repeat(text: str, times: int) -> str: return text * times `} />
In this example:
text and times should be. The * operator works differently depending on types (string repetition, list repetition, or multiplication).text should be a string, times should be an integer, and the function returns a string.times or using the function incorrectly.class Rectangle:
width: int
height: int
def __init__(self, width: int, height: int) -> None:
self.width = width
self.height = height
rect = Rectangle(width=100, height=50)
area = rect.width * rect.hieght
print(area)
In this example:
rect.hieght (should be rect.height).Rectangle class has defined attributes width and height, but not hieght.Spelling is hard! Let's add the dataclass decorator to our class definition. This will generate a constructor for us, and also add a few other useful methods.
<CodeSnippet sampleFilename="misspelled.py" codeSample={`#Pyrefly will catch this spelling error before you run the code from dataclasses import dataclass
@dataclass class Rectangle: width: int height: int
rect = Rectangle(width=100, height=50)
area = rect.width * rect.hieght `} />
In this dataclass example:
@dataclass decorator automatically generates methods like __init__, __repr__, and __eq__ based on the class attributes.width: int, height: int).rect.hieght), but tools like Pyrefly can catch this before runtime because the dataclass clearly defines which attributes exist.Since Python 3.9 you can use all the primitive types directly as annotations.
<CodeSnippet
sampleFilename="primitives_types.py"
codeSample={age: int = 30 height: float = 1.85 name: str = "Tyler Typer" is_admin: bool = False }
/>
In this primitive types example:
int, float, str, bool).age + name since adding an integer and string isn't a valid operation.You can also specify a parameter as optional by using Optional type, or now with the | None syntax.
<CodeSnippet sampleFilename="primitive2_types.py" codeSample={`# Optional typing example
from typing import Optional
middle_name: Optional[str] = None # classic nickname: str | None = None # 3.10+ shorthand `} />
In this Optional type example:
None.Optional[str] is the traditional syntax (pre-Python 3.10).str | None is the newer union syntax introduced in Python 3.10.None, so they can warn you if you try to perform string operations without checking for None first.list[int] scores: list[int] = [98, 87, 91]tuple[float, float] point: tuple[float, float] = (3.0, 4.0)dict[str, int] inventory: dict[str, int] = {"apples": 5}set[str] authors: set[str] = {"Bob", "Eve"}Since Python 3.9 you can subscript built‑ins directly—no need for from typing import List.
<CodeSnippet
sampleFilename="basic_function_types.py"
codeSample={# Simple function def add(a: int, b: int) -> int: return a + b }
/>
In this basic function example:
a and b are annotated as integers.-> int).Default values keep their annotation:
<CodeSnippet sampleFilename="default_value_types.py" codeSample={`# Function with default value
def greet(name: str, polite: bool = True) -> str: return "Hello!" if polite else f"Yo {name}" `} />
In this function with default values:
name parameter must be a string.polite parameter is a boolean with a default value of True.polite has a default value, it still has a type annotation to ensure that if it's explicitly provided, it must be a boolean.Variable‑length arguments:
<CodeSnippet sampleFilename="variable_length_types.py" codeSample={`# Variable length functions from collections.abc import Callable
Logger = Callable[[str], None]
def debug(*msgs: str, log: Logger | None) -> None: for m in msgs: if log is not None: log(m) else: print(m) `} />
In this variable-length arguments example:
Logger is defined as a type alias for a callable that takes a string and returns nothing (None).*msgs: str indicates that the function accepts any number of string arguments.log: Logger | None means the log parameter can be either a Logger function or None.None.You can download the Pyrefly extension for VSCode to get type hint signals directly in your IDE.
Next, install Pyrefly and check some code:
# Fast, zero‑config
pip install pyrefly
pyrefly check ./my_sample.py
# Check whole directories
pyrefly check app/ tests/
Create a pyrefly.toml file to configure your project. Instructions here.