Say you write a small generic store, Repo[T], and almost every caller
stores strings. Under mypy --strict, each caller must still write
Repo[str], because a bare Repo is an error:
class Repo[T]:
def __init__(self, value: T | None = None) -> None:
self.value = value
def load(repo: Repo) -> None: # error: Missing type arguments for generic type "Repo"
...
Python 3.13 lets you give the type parameter a default type. mypy uses the default when the caller leaves the type out and there is nothing to infer it from.
Write the default after an equals sign
This example needs Python 3.13 or newer:
from typing import assert_type
class Repo[T = str]:
def __init__(self, value: T | None = None) -> None:
self.value = value
def load(repo: Repo) -> None: # fine: a bare Repo means Repo[str]
...
assert_type(Repo(), Repo[str])
assert_type(Repo(7), Repo[int])
assert_type(Repo[int](), Repo[int])
assert_type asks mypy to confirm a type, and does nothing at runtime.
Repo() passes nothing to infer T from, so mypy uses the default, str.
Repo(7) passes an int, so mypy infers int. An explicit Repo[int] takes
priority over the default and over inference. The default is only a type for mypy, so Repo().value is still None.
Functions and type aliases can have defaults too:
from typing import assert_type
type Pair[T = int] = tuple[T, T]
def identity[T = bytes](value: T) -> T:
return value
pair: Pair = (1, 2) # Pair means tuple[int, int]
assert_type(identity("hello"), str)
mypy infers str for identity("hello") from the argument, and ignores the
default.
Put the defaults last
Type parameters with defaults go after all the others, like default values for function arguments:
class Options[T, U = int, V = str]:
pass
options: Options[bool] = Options() # Options[bool, int, str]
both: Options[bool, float] # Options[bool, float, str]
class Bad[T = int, U]: ... is a SyntaxError.
Check your understanding Why must type parameters with defaults come after those without one? Show answer Hide answer
Type arguments fill the parameters from left to right. In Bad[T = int, U],
the first argument you write always goes to T. To give U a type, you must
also write a type for T, so mypy never uses the default for T. With
the defaults last, you leave arguments off the end, and the defaults fill the
gaps.
Support Python 3.12 with typing_extensions
The [T = str] syntax is a SyntaxError on Python 3.12. To support 3.12, use
the older TypeVar spelling from the article "Generic Functions from Zero".
Import TypeVar from the typing_extensions package, which you install with
pip:
from typing import Generic, assert_type
from typing_extensions import TypeVar
Value = TypeVar("Value", default=str)
class LegacyRepo(Generic[Value]):
def __init__(self, value: Value | None = None) -> None:
self.value = value
assert_type(LegacyRepo(), LegacyRepo[str])
assert_type(LegacyRepo(7), LegacyRepo[int])
The TypeVar in typing accepts default only from Python 3.13. On 3.12 it
raises TypeError.
Check your understanding
What does class TextRepo(Repo) inherit when Repo has the default str?
Show answer
Hide answer
It inherits from Repo[str], because a bare Repo means Repo[str]. So
TextRepo().value has the type str | None. Without the default,
mypy --strict rejects the bare Repo in the class line, with the same
missing-type-arguments error as before.
Pick a default only when it has one clear meaning
Add a default when leaving the type out has one clear meaning, as str does
for Repo. Keep the parameter required when callers should choose. Avoid Any
as a default, because it turns off checking for every caller who leaves the
type out. Adding a default to a class that people already use changes what a
bare annotation and a bare base class mean. Check those uses before you
release the change.
Going further
- Python 3.13 can also report a default at runtime.
T.has_default()says whether a type parameter has one, andT.__default__holds it, ortyping.NoDefaultwhen there is none. - A
TypeVarTuplesuch as*Tstakes any number of types, and its own rules apply. A type parameter right after*Tscannot have a default. - Read PEP 696 and the
typing specification
for the rules on bounds, constraints and
TypeVarTuple.