Class types
Every class is also a valid type. Any instance of a subclass is compatible with all superclasses:
class A :
def f ( self ) -> int :
return 2
class B ( A ):
def f ( self ) -> int :
return 3
def g ( self ) -> int :
return 4
def foo ( a : A) -> None :
print (a.f()) # OK
a.g() # Error: "A" has no attribute "g"
foo(B()) # OK (B is a subclass of A)
The Any type
A value with the Any type is dynamically typed. Mypy doesn’t know anything about the possible runtime types of such value.
from typing import Any
a: Any = None
s: str = ''
a = 2 # OK (assign "int" to "Any")
s = a # OK (assign "Any" to "str")
You need to be careful with Any types, since they let you lie to mypy. This could easily hide bugs.
Any vs object
Any should not be confused with object. Unlike object, Any introduces type unsafety:
Any is compatible with every type, and vice versa
object is compatible with every type, but other types are not compatible with object
def show_heading ( s : object ) -> None :
print ( '=== ' + s + ' ===' ) # Error: can't concatenate object and str
show_heading( 1 ) # OK (runtime error only)
Tuple types
The type tuple[T1, ..., Tn] represents a tuple with specific item types:
Fixed-length tuples
Variable-length tuples
def f ( t : tuple[ int , str ]) -> None :
t = 1 , 'foo' # OK
t = 'foo' , 1 # Error: Type check error
Usually it’s better to use Sequence[T] instead of tuple[T, ...], as Sequence is also compatible with lists and other non-tuple sequences.
Callable types
You can pass around function objects and bound methods in statically typed code. The type of a function is Callable[[A1, ..., An], Rt]:
from collections.abc import Callable
def twice ( i : int , next : Callable[[ int ], int ]) -> int :
return next ( next (i))
def add ( i : int ) -> int :
return i + 1
print (twice( 3 , add)) # 5
Flexible callable types
You can use Callable[..., T] for less typical cases:
from collections.abc import Callable
def arbitrary_call ( f : Callable[ ... , int ]) -> int :
return f( 'x' ) + f( y = 2 ) # OK
arbitrary_call( ord ) # No static error, but fails at runtime
Union types
Python functions often accept values of two or more different types. Use T1 | T2 | ... | Tn to construct a union type:
def f ( x : int | str ) -> None :
x + 1 # Error: str + int is not valid
if isinstance (x, int ):
# Here type of x is int
x + 1 # OK
else :
# Here type of x is str
x + 'a' # OK
f( 1 ) # OK
f( 'x' ) # OK
f( 1.1 ) # Error
Operations are valid for union types only if they are valid for every union item. This is why isinstance checks are often necessary.
Legacy union syntax
For Python 3.9 and older, use Union[T1, T2, ...]:
from typing import Union
def f ( x : Union[ int , str ]) -> None :
...
Optional types and None
You can use T | None to define a type variant that allows None:
def strlen ( s : str ) -> int | None :
if not s:
return None # OK
return len (s)
Python 3.10+
Python 3.9 and earlier
def my_inc ( x : int | None ) -> int :
if x is None :
return 0
else :
return x + 1
from typing import Optional
def my_inc ( x : Optional[ int ]) -> int :
if x is None :
return 0
else :
return x + 1
None checks
Mypy recognizes several None checks:
is None check
if x check
Logical expressions
if x is None :
return 0
else :
return x + 1 # x is int here
Type aliases
Type names can be long. You can define a type alias by assigning the type to a variable:
Python 3.12+
Python 3.10-3.11
Python 3.9 and earlier
type AliasType = list[dict[tuple[ int , str ], set[ int ]]] | tuple[ str , list[ str ]]
def f () -> AliasType:
...
from typing import TypeAlias
AliasType: TypeAlias = list[dict[tuple[ int , str ], set[ int ]]] | tuple[ str , list[ str ]]
def f () -> AliasType:
...
# Implicit type alias
AliasType = list[dict[tuple[ int , str ], set[ int ]]] | tuple[ str , list[ str ]]
def f () -> AliasType:
...
A type alias doesn’t create a new type. It’s just a shorthand notation for another type.
Named tuples
Mypy recognizes named tuples and can type check code that uses them:
from typing import NamedTuple
class Point ( NamedTuple ):
x: int
y: int
p = Point( x = 1 , y = 'x' ) # Error: Argument has incompatible type "str"
from typing import NamedTuple
Point = NamedTuple( 'Point' , [( 'x' , int ), ( 'y' , int )])
p = Point( x = 1 , y = 'x' ) # Error: Argument has incompatible type "str"
The type of class objects
Use type[C] to refer to a class object deriving from C:
class User :
pass
class BasicUser ( User ):
def upgrade ( self ):
"""Upgrade to Pro"""
class ProUser ( User ):
def pay ( self ):
"""Pay bill"""
def new_user[U: User](user_class: type[U]) -> U:
return user_class()
beginner = new_user(BasicUser) # Inferred type is BasicUser
beginner.upgrade() # OK
The value corresponding to type[C] must be an actual class object that’s a subtype of C.
Generators
A basic generator that only yields values can be annotated as Iterator[YieldType] or Iterable[YieldType]:
from typing import Iterator
def squares ( n : int ) -> Iterator[ int ]:
for i in range (n):
yield i * i
If your generator accepts values via send() or returns a value, use Generator[YieldType, SendType, ReturnType]:
from typing import Generator
def echo_round () -> Generator[ int , float , str ]:
sent = yield 0
while sent >= 0 :
sent = yield round (sent)
return 'Done'