typing — Support for type hints — Python 3.11.0 documentation

typing

— Support for type hints¶

New in version 3.5.

Source code: Lib/typing.py

Note

The Python runtime does not enforce function and variable type annotations.
They can be used by third party tools such as type checkers, IDEs, linters,
etc.

This module provides runtime support for type hints. The most fundamental
support consists of the types Any, Union, Callable,
TypeVar, and Generic. For a full specification, please see
PEP 484. For a simplified introduction to type hints, see PEP 483.

The function below takes and returns a string and is annotated as follows:

def

greeting

(

name

:

str

)

->

str

:

return

'Hello '

+

name

In the function greeting, the argument name is expected to be of type
str and the return type str. Subtypes are accepted as
arguments.

New features are frequently added to the typing module.
The typing_extensions package
provides backports of these new features to older versions of Python.

For a summary of deprecated features and a deprecation timeline, please see
Deprecation Timeline of Major Features.

See also

The documentation at https://typing.readthedocs.io/ serves as useful reference
for type system features, useful typing related tools and typing best practices.

Type aliases¶

A type alias is defined by assigning the type to the alias. In this example,
Vector and list[float] will be treated as interchangeable synonyms:

Vector

=

list

[

float

]

def

scale

(

scalar

:

float

,

vector

:

Vector

)

->

Vector

:

return

[

scalar

*

num

for

num

in

vector

]

# passes type checking; a list of floats qualifies as a Vector.

new_vector

=

scale

(

2.0

,

[

1.0

,

-

4.2

,

5.4

])

Type aliases are useful for simplifying complex type signatures. For example:

from

collections.abc

import

Sequence

ConnectionOptions

=

dict

[

str

,

str

]

Address

=

tuple

[

str

,

int

]

Server

=

tuple

[

Address

,

ConnectionOptions

]

def

broadcast_message

(

message

:

str

,

servers

:

Sequence

[

Server

])

->

None

:

...

# The static type checker will treat the previous type signature as

# being exactly equivalent to this one.

def

broadcast_message

(

message

:

str

,

servers

:

Sequence

[

tuple

[

tuple

[

str

,

int

],

dict

[

str

,

str

]]])

->

None

:

...

Note that None as a type hint is a special case and is replaced by
type(None).

NewType¶

Use the NewType helper to create distinct types:

from

typing

import

NewType

UserId

=

NewType

(

'UserId'

,

int

)

some_id

=

UserId

(

524313

)

The static type checker will treat the new type as if it were a subclass
of the original type. This is useful in helping catch logical errors:

def

get_user_name

(

user_id

:

UserId

)

->

str

:

...

# passes type checking

user_a

=

get_user_name

(

UserId

(

42351

))

# fails type checking; an int is not a UserId

user_b

=

get_user_name

(

-

1

)

You may still perform all int operations on a variable of type UserId,
but the result will always be of type int. This lets you pass in a
UserId wherever an int might be expected, but will prevent you from
accidentally creating a UserId in an invalid way:

# 'output' is of type 'int', not 'UserId'

output

=

UserId

(

23413

)

+

UserId

(

54341

)

Note that these checks are enforced only by the static type checker. At runtime,
the statement Derived = NewType('Derived', Base) will make Derived a
callable that immediately returns whatever parameter you pass it. That means
the expression Derived(some_value) does not create a new class or introduce
much overhead beyond that of a regular function call.

More precisely, the expression some_value is Derived(some_value) is always
true at runtime.

It is invalid to create a subtype of Derived:

from

typing

import

NewType

UserId

=

NewType

(

'UserId'

,

int

)

# Fails at runtime and does not pass type checking

class

AdminUserId

(

UserId

):

pass

However, it is possible to create a NewType based on a ‘derived’ NewType:

from

typing

import

NewType

UserId

=

NewType

(

'UserId'

,

int

)

ProUserId

=

NewType

(

'ProUserId'

,

UserId

)

and typechecking for ProUserId will work as expected.

See PEP 484 for more details.

Note

Recall that the use of a type alias declares two types to be equivalent to
one another. Doing Alias = Original will make the static type checker
treat Alias as being exactly equivalent to Original in all cases.
This is useful when you want to simplify complex type signatures.

In contrast, NewType declares one type to be a subtype of another.
Doing Derived = NewType('Derived', Original) will make the static type
checker treat Derived as a subclass of Original, which means a
value of type Original cannot be used in places where a value of type
Derived is expected. This is useful when you want to prevent logic
errors with minimal runtime cost.

New in version 3.5.2.

Changed in version 3.10: NewType is now a class rather than a function. There is some additional
runtime cost when calling NewType over a regular function. However, this
cost will be reduced in 3.11.0.

Callable¶

Frameworks expecting callback functions of specific signatures might be
type hinted using Callable[[Arg1Type, Arg2Type], ReturnType].

For example:

from

collections.abc

import

Callable

def

feeder

(

get_next_item

:

Callable

[[],

str

])

->

None

:

# Body

def

async_query

(

on_success

:

Callable

[[

int

],

None

],

on_error

:

Callable

[[

int

,

Exception

],

None

])

->

None

:

# Body

async

def

on_update

(

value

:

str

)

->

None

:

# Body

callback

:

Callable

[[

str

],

Awaitable

[

None

]]

=

on_update

It is possible to declare the return type of a callable without specifying
the call signature by substituting a literal ellipsis
for the list of arguments in the type hint: Callable[..., ReturnType].

Callables which take other callables as arguments may indicate that their
parameter types are dependent on each other using ParamSpec.
Additionally, if that callable adds or removes arguments from other
callables, the Concatenate operator may be used. They
take the form Callable[ParamSpecVariable, ReturnType] and
Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]
respectively.

Changed in version 3.10: Callable now supports ParamSpec and Concatenate.
See PEP 612 for more details.

See also

The documentation for ParamSpec and Concatenate provides
examples of usage in Callable.

Generics¶

Since type information about objects kept in containers cannot be statically
inferred in a generic way, abstract base classes have been extended to support
subscription to denote expected types for container elements.

from

collections.abc

import

Mapping

,

Sequence

def

notify_by_email

(

employees

:

Sequence

[

Employee

],

overrides

:

Mapping

[

str

,

str

])

->

None

:

...

Generics can be parameterized by using a factory available in typing
called TypeVar.

from

collections.abc

import

Sequence

from

typing

import

TypeVar

T

=

TypeVar

(

'T'

)

# Declare type variable

def

first

(

l

:

Sequence

[

T

])

->

T

:

# Generic function

return

l

[

0

]

User-defined generic types¶

A user-defined class can be defined as a generic class.

from

typing

import

TypeVar

,

Generic

from

logging

import

Logger

T

=

TypeVar

(

'T'

)

class

LoggedVar

(

Generic

[

T

]):

def

__init__

(

self

,

value

:

T

,

name

:

str

,

logger

:

Logger

)

->

None

:

self

.

name

=

name

self

.

logger

=

logger

self

.

value

=

value

def

set

(

self

,

new

:

T

)

->

None

:

self

.

log

(

'Set '

+

repr

(

self

.

value

))

self

.

value

=

new

def

get

(

self

)

->

T

:

self

.

log

(

'Get '

+

repr

(

self

.

value

))

return

self

.

value

def

log

(

self

,

message

:

str

)

->

None

:

self

.

logger

.

info

(

'

%s

:

%s

'

,

self

.

name

,

message

)

Generic[T] as a base class defines that the class LoggedVar takes a
single type parameter T . This also makes T valid as a type within the
class body.

The Generic base class defines __class_getitem__() so
that LoggedVar[T] is valid as a type:

from

collections.abc

import

Iterable

def

zero_all_vars

(

vars

:

Iterable

[

LoggedVar

[

int

]])

->

None

:

for

var

in

vars

:

var

.

set

(

0

)

A generic type can have any number of type variables. All varieties of
TypeVar are permissible as parameters for a generic type:

from

typing

import

TypeVar

,

Generic

,

Sequence

T

=

TypeVar

(

'T'

,

contravariant

=

True

)

B

=

TypeVar

(

'B'

,

bound

=

Sequence

[

bytes

],

covariant

=

True

)

S

=

TypeVar

(

'S'

,

int

,

str

)

class

WeirdTrio

(

Generic

[

T

,

B

,

S

]):

...

Each type variable argument to Generic must be distinct.
This is thus invalid:

from

typing

import

TypeVar

,

Generic

...

T

=

TypeVar

(

'T'

)

class

Pair

(

Generic

[

T

,

T

]):

# INVALID

...

You can use multiple inheritance with Generic:

from

collections.abc

import

Sized

from

typing

import

TypeVar

,

Generic

T

=

TypeVar

(

'T'

)

class

LinkedList

(

Sized

,

Generic

[

T

]):

...

When inheriting from generic classes, some type variables could be fixed:

from

collections.abc

import

Mapping

from

typing

import

TypeVar

T

=

TypeVar

(

'T'

)

class

MyDict

(

Mapping

[

str

,

T

]):

...

In this case MyDict has a single parameter, T.

Using a generic class without specifying type parameters assumes
Any for each position. In the following example, MyIterable is
not generic but implicitly inherits from Iterable[Any]:

from

collections.abc

import

Iterable

class

MyIterable

(

Iterable

):

# Same as Iterable[Any]

User defined generic type aliases are also supported. Examples:

from

collections.abc

import

Iterable

from

typing

import

TypeVar

S

=

TypeVar

(

'S'

)

Response

=

Iterable

[

S

]

|

int

# Return type here is same as Iterable[str] | int

def

response

(

query

:

str

)

->

Response

[

str

]:

...

T

=

TypeVar

(

'T'

,

int

,

float

,

complex

)

Vec

=

Iterable

[

tuple

[

T

,

T

]]

def

inproduct

(

v

:

Vec

[

T

])

->

T

:

# Same as Iterable[tuple[T, T]]

return

sum

(

x

*

y

for

x

,

y

in

v

)

Changed in version 3.7: Generic no longer has a custom metaclass.

User-defined generics for parameter expressions are also supported via parameter
specification variables in the form Generic[P]. The behavior is consistent
with type variables’ described above as parameter specification variables are
treated by the typing module as a specialized type variable. The one exception
to this is that a list of types can be used to substitute a ParamSpec:

>>>

from

typing

import

Generic

,

ParamSpec

,

TypeVar

>>>

T

=

TypeVar

(

'T'

)

>>>

P

=

ParamSpec

(

'P'

)

>>>

class

Z

(

Generic

[

T

,

P

]):

...

...

>>>

Z

[

int

,

[

dict

,

float

]]

__main__.Z[int, (<class 'dict'>, <class 'float'>)]

Furthermore, a generic with only one parameter specification variable will accept
parameter lists in the forms X[[Type1, Type2, ...]] and also
X[Type1, Type2, ...] for aesthetic reasons. Internally, the latter is converted
to the former, so the following are equivalent:

>>>

class

X

(

Generic

[

P

]):

...

...

>>>

X

[

int

,

str

]

__main__.X[(<class 'int'>, <class 'str'>)]

>>>

X

[[

int

,

str

]]

__main__.X[(<class 'int'>, <class 'str'>)]

Do note that generics with ParamSpec may not have correct
__parameters__ after substitution in some cases because they
are intended primarily for static type checking.

Changed in version 3.10: Generic can now be parameterized over parameter expressions.
See ParamSpec and PEP 612 for more details.

A user-defined generic class can have ABCs as base classes without a metaclass
conflict. Generic metaclasses are not supported. The outcome of parameterizing
generics is cached, and most types in the typing module are hashable and
comparable for equality.

The

Any

type¶

A special kind of type is Any. A static type checker will treat
every type as being compatible with Any and Any as being
compatible with every type.

This means that it is possible to perform any operation or method call on a
value of type Any and assign it to any variable:

from

typing

import

Any

a

:

Any

=

None

a

=

[]

# OK

a

=

2

# OK

s

:

str

=

''

s

=

a

# OK

def

foo

(

item

:

Any

)

->

int

:

# Passes type checking; 'item' could be any type,

# and that type might have a 'bar' method

item

.

bar

()

...

Notice that no type checking is performed when assigning a value of type
Any to a more precise type. For example, the static type checker did
not report an error when assigning a to s even though s was
declared to be of type str and receives an int value at
runtime!

Furthermore, all functions without a return type or parameter types will
implicitly default to using Any:

def

legacy_parser

(

text

):

...

return

data

# A static type checker will treat the above

# as having the same signature as:

def

legacy_parser

(

text

:

Any

)

->

Any

:

...

return

data

This behavior allows Any to be used as an escape hatch when you
need to mix dynamically and statically typed code.

Contrast the behavior of Any with the behavior of object.
Similar to Any, every type is a subtype of object. However,
unlike Any, the reverse is not true: object is not a
subtype of every other type.

That means when the type of a value is object, a type checker will
reject almost all operations on it, and assigning it to a variable (or using
it as a return value) of a more specialized type is a type error. For example:

def

hash_a

(

item

:

object

)

->

int

:

# Fails type checking; an object does not have a 'magic' method.

item

.

magic

()

...

def

hash_b

(

item

:

Any

)

->

int

:

# Passes type checking

item

.

magic

()

...

# Passes type checking, since ints and strs are subclasses of object

hash_a

(

42

)

hash_a

(

"foo"

)

# Passes type checking, since Any is compatible with all types

hash_b

(

42

)

hash_b

(

"foo"

)

Use object to indicate that a value could be any type in a typesafe
manner. Use Any to indicate that a value is dynamically typed.

Nominal vs structural subtyping¶

Initially PEP 484 defined the Python static type system as using
nominal subtyping. This means that a class A is allowed where
a class B is expected if and only if A is a subclass of B.

This requirement previously also applied to abstract base classes, such as
Iterable. The problem with this approach is that a class had
to be explicitly marked to support them, which is unpythonic and unlike
what one would normally do in idiomatic dynamically typed Python code.
For example, this conforms to PEP 484:

from

collections.abc

import

Sized

,

Iterable

,

Iterator

class

Bucket

(

Sized

,

Iterable

[

int

]):

...

def

__len__

(

self

)

->

int

:

...

def

__iter__

(

self

)

->

Iterator

[

int

]:

...

PEP 544 allows to solve this problem by allowing users to write
the above code without explicit base classes in the class definition,
allowing Bucket to be implicitly considered a subtype of both Sized
and Iterable[int] by static type checkers. This is known as
structural subtyping (or static duck-typing):

from

collections.abc

import

Iterator

,

Iterable

class

Bucket

:

# Note: no base classes

...

def

__len__

(

self

)

->

int

:

...

def

__iter__

(

self

)

->

Iterator

[

int

]:

...

def

collect

(

items

:

Iterable

[

int

])

->

int

:

...

result

=

collect

(

Bucket

())

# Passes type check

Moreover, by subclassing a special class Protocol, a user
can define new custom protocols to fully enjoy structural subtyping
(see examples below).

Deprecation Timeline of Major Features¶

Certain features in typing are deprecated and may be removed in a future
version of Python. The following table summarizes major deprecations for your
convenience. This is subject to change, and not all deprecations are listed.

Feature

Deprecated in

Projected removal

PEP/issue

typing.io and typing.re
submodules

3.8

3.13

bpo-38291

typing versions of standard
collections

3.9

Undecided

PEP 585

typing.Text

3.11

Undecided

gh-92332