Tóm Tắt
typing
— Support for type hints¶
typing
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¶
Any
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