Added in version 3.5.
源代码:
Lib/typing.py
注意
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.
Consider the function below:
def surface_area_of_cube(edge_length: float) -> str:
return f"The surface area of the cube is {6 * edge_length ** 2}."
函数
surface_area_of_cube
takes an argument expected to be an instance of
float
, as indicated by the
类型提示
edge_length: float
. The function is expected to return an instance of
str
, as indicated by the
-> str
hint.
While type hints can be simple classes like
float
or
str
, they can also be more complex. The
typing
module provides a vocabulary of more advanced type hints.
New features are frequently added to the
typing
模块。
typing_extensions
package provides backports of these new features to older versions of Python.
另请参阅
-
“Typing cheat sheet”
-
A quick overview of type hints (hosted at the mypy docs)
-
“Type System Reference” section of
the mypy docs
-
The Python typing system is standardised via PEPs, so this reference should broadly apply to most Python type checkers. (Some parts may still be specific to mypy.)
-
“Static Typing with Python”
-
Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices.
类型别名
¶
A type alias is defined using the
type
statement, which creates an instance of
TypeAliasType
. In this example,
Vector
and
list[float]
will be treated equivalently by static type checkers:
type 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])
类型别名很有用为简化复杂类型签名。例如:
from collections.abc import Sequence
type ConnectionOptions = dict[str, str]
type Address = tuple[str, int]
type 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:
...
The
type
statement is new in Python 3.12. For backwards compatibility, type aliases can also be created through simple assignment:
Vector = list[float]
Or marked with
TypeAlias
to make it explicit that this is a type alias, not a normal variable assignment:
from typing import TypeAlias
Vector: TypeAlias = list[float]
NewType
¶
使用
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)
可以仍然履行所有
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.
见
PEP 484
了解更多细节。
注意
Recall that the use of a type alias declares two types to be
equivalent
to one another. Doing
type 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
作为
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.
Added in version 3.5.2.
3.10 版改变:
NewType
is now a class rather than a function. As a result, there is some additional runtime cost when calling
NewType
over a regular function.
3.11 版改变:
The performance of calling
NewType
has been restored to its level in Python 3.9.
Annotating callable objects
¶
Functions – or other
callable
objects – can be annotated using
collections.abc.Callable
or deprecated
typing.Callable
.
Callable[[int], str]
signifies a function that takes a single parameter of type
int
并返回
str
.
例如:
from collections.abc import Callable, Awaitable
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
The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types, a
ParamSpec
,
Concatenate
, or an ellipsis. The return type must be a single type.
If a literal ellipsis
...
is given as the argument list, it indicates that a callable with any arbitrary parameter list would be acceptable:
def concat(x: str, y: str) -> str:
return x + y
x: Callable[..., str]
x = str # OK
x = concat # Also OK
Callable
cannot express complex signatures such as functions that take a variadic number of arguments,
overloaded functions
, or functions that have keyword-only parameters. However, these signatures can be expressed by defining a
Protocol
class with a
__call__()
方法:
from collections.abc import Iterable
from typing import Protocol
class Combiner(Protocol):
def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ...
def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes:
for item in data:
...
def good_cb(*vals: bytes, maxlen: int | None = None) -> list[bytes]:
...
def bad_cb(*vals: bytes, maxitems: int | None) -> list[bytes]:
...
batch_proc([], good_cb) # OK
batch_proc([], bad_cb) # Error! Argument 2 has incompatible type because of
# different name and kind in the callback
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]
分别。
3.10 版改变:
Callable
现在支持
ParamSpec
and
Concatenate
。见
PEP 612
了解更多细节。
一般
¶
Since type information about objects kept in containers cannot be statically inferred in a generic way, many container classes in the standard library support subscription to denote the expected types of container elements.
from collections.abc import Mapping, Sequence
class Employee: ...
# Sequence[Employee] indicates that all elements in the sequence
# must be instances of "Employee".
# Mapping[str, str] indicates that all keys and all values in the mapping
# must be strings.
def notify_by_email(employees: Sequence[Employee],
overrides: Mapping[str, str]) -> None: ...
Generic functions and classes can be parameterized by using
type parameter syntax
:
from collections.abc import Sequence
def first[T](l: Sequence[T]) -> T: # Function is generic over the TypeVar "T"
return l[0]
Or by using the
TypeVar
factory directly:
from collections.abc import Sequence
from typing import TypeVar
U = TypeVar('U') # Declare type variable "U"
def second(l: Sequence[U]) -> U: # Function is generic over the TypeVar "U"
return l[1]
3.12 版改变:
Syntactic support for generics is new in Python 3.12.
Annotating tuples
¶
For most containers in Python, the typing system assumes that all elements in the container will be of the same type. For example:
from collections.abc import Mapping
# Type checker will infer that all elements in ``x`` are meant to be ints
x: list[int] = []
# Type checker error: ``list`` only accepts a single type argument:
y: list[int, str] = [1, 'foo']
# Type checker will infer that all keys in ``z`` are meant to be strings,
# and that all values in ``z`` are meant to be either strings or ints
z: Mapping[str, str | int] = {}
list
only accepts one type argument, so a type checker would emit an error on the
y
assignment above. Similarly,
Mapping
only accepts two type arguments: the first indicates the type of the keys, and the second indicates the type of the values.
Unlike most other Python containers, however, it is common in idiomatic Python code for tuples to have elements which are not all of the same type. For this reason, tuples are special-cased in Python’s typing system.
tuple
接受
any number
of type arguments:
# OK: ``x`` is assigned to a tuple of length 1 where the sole element is an int
x: tuple[int] = (5,)
# OK: ``y`` is assigned to a tuple of length 2;
# element 1 is an int, element 2 is a str
y: tuple[int, str] = (5, "foo")
# Error: the type annotation indicates a tuple of length 1,
# but ``z`` has been assigned to a tuple of length 3
z: tuple[int] = (1, 2, 3)
To denote a tuple which could be of
any
length, and in which all elements are of the same type
T
,使用
tuple[T, ...]
. To denote an empty tuple, use
tuple[()]
. Using plain
tuple
as an annotation is equivalent to using
tuple[Any, ...]
:
x: tuple[int, ...] = (1, 2)
# These reassignments are OK: ``tuple[int, ...]`` indicates x can be of any length
x = (1, 2, 3)
x = ()
# This reassignment is an error: all elements in ``x`` must be ints
x = ("foo", "bar")
# ``y`` can only ever be assigned to an empty tuple
y: tuple[()] = ()
z: tuple = ("foo", "bar")
# These reassignments are OK: plain ``tuple`` is equivalent to ``tuple[Any, ...]``
z = (1, 2, 3)
z = ()
The type of class objects
¶
A variable annotated with
C
may accept a value of type
C
. In contrast, a variable annotated with
type[C]
(or deprecated
typing.Type[C]
) may accept values that are classes themselves – specifically, it will accept the
class object
of
C
。例如:
a = 3 # Has type ``int``
b = int # Has type ``type[int]``
c = type(a) # Also has type ``type[int]``
注意,
type[C]
是协变:
class User: ...
class ProUser(User): ...
class TeamUser(User): ...
def make_new_user(user_class: type[User]) -> User:
# ...
return user_class()
make_new_user(User) # OK
make_new_user(ProUser) # Also OK: ``type[ProUser]`` is a subtype of ``type[User]``
make_new_user(TeamUser) # Still fine
make_new_user(User()) # Error: expected ``type[User]`` but got ``User``
make_new_user(int) # Error: ``type[int]`` is not a subtype of ``type[User]``
The only legal parameters for
type
are classes,
Any
,
类型变量
, and unions of any of these types. For example:
def new_non_team_user(user_class: type[BasicUser | ProUser]): ...
new_non_team_user(BasicUser) # OK
new_non_team_user(ProUser) # OK
new_non_team_user(TeamUser) # Error: ``type[TeamUser]`` is not a subtype
# of ``type[BasicUser | ProUser]``
new_non_team_user(User) # Also an error
type[Any]
相当于
type
, which is the root of Python’s
metaclass hierarchy
.
Annotating generators and coroutines
¶
A generator can be annotated using the generic type
Generator[YieldType, SendType, ReturnType]
。例如:
def echo_round() -> Generator[int, float, str]:
sent = yield 0
while sent >= 0:
sent = yield round(sent)
return 'Done'
Note that unlike many other generic classes in the standard library, the
SendType
of
Generator
behaves contravariantly, not covariantly or invariantly.
The
SendType
and
ReturnType
parameters default to
None
:
def infinite_stream(start: int) -> Generator[int]:
while True:
yield start
start += 1
It is also possible to set these types explicitly:
def infinite_stream(start: int) -> Generator[int, None, None]:
while True:
yield start
start += 1
Simple generators that only ever yield values can also be annotated as having a return type of either
Iterable[YieldType]
or
Iterator[YieldType]
:
def infinite_stream(start: int) -> Iterator[int]:
while True:
yield start
start += 1
Async generators are handled in a similar fashion, but don’t expect a
ReturnType
type argument (
AsyncGenerator[YieldType, SendType]
)。
SendType
自变量默认为
None
, so the following definitions are equivalent:
async def infinite_stream(start: int) -> AsyncGenerator[int]:
while True:
yield start
start = await increment(start)
async def infinite_stream(start: int) -> AsyncGenerator[int, None]:
while True:
yield start
start = await increment(start)
As in the synchronous case,
AsyncIterable[YieldType]
and
AsyncIterator[YieldType]
are available as well:
async def infinite_stream(start: int) -> AsyncIterator[int]:
while True:
yield start
start = await increment(start)
Coroutines can be annotated using
Coroutine[YieldType, SendType, ReturnType]
. Generic arguments correspond to those of
Generator
,例如:
from collections.abc import Coroutine
c: Coroutine[list[str], str, int] # Some coroutine defined elsewhere
x = c.send('hi') # Inferred type of 'x' is list[str]
async def bar() -> None:
y = await c # Inferred type of 'y' is int
用户定义一般类型
¶
A user-defined class can be defined as a generic class.
from logging import Logger
class LoggedVar[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)
This syntax indicates that the class
LoggedVar
is parameterised around a single
type variable
T
. This also makes
T
valid as a type within the class body.
Generic classes implicitly inherit from
Generic
. For compatibility with Python 3.11 and lower, it is also possible to inherit explicitly from
Generic
to indicate a generic class:
from typing import TypeVar, Generic
T = TypeVar('T')
class LoggedVar(Generic[T]):
...
Generic classes have
__class_getitem__()
methods, meaning they can be parameterised at runtime (e.g.
LoggedVar[int]
below):
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
class WeirdTrio[T, B: Sequence[bytes], S: (int, str)]:
...
OldT = TypeVar('OldT', contravariant=True)
OldB = TypeVar('OldB', bound=Sequence[bytes], covariant=True)
OldS = TypeVar('OldS', int, str)
class OldWeirdTrio(Generic[OldT, OldB, OldS]):
...
Each type variable argument to
Generic
must be distinct. This is thus invalid:
from typing import TypeVar, Generic
...
class Pair[M, M]: # SyntaxError
...
T = TypeVar('T')
class Pair(Generic[T, T]): # INVALID
...
Generic classes can also inherit from other classes:
from collections.abc import Sized
class LinkedList[T](Sized):
...
When inheriting from generic classes, some type parameters could be fixed:
from collections.abc import Mapping
class MyDict[T](Mapping[str, T]):
...
在此情况下
MyDict
拥有单参数,
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
type Response[S] = Iterable[S] | int
# Return type here is same as Iterable[str] | int
def response(query: str) -> Response[str]:
...
type Vec[T] = Iterable[tuple[T, T]]
def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
return sum(x*y for x, y in v)
For backward compatibility, generic type aliases can also be created through a simple assignment:
from collections.abc import Iterable
from typing import TypeVar
S = TypeVar("S")
Response = Iterable[S] | int
3.7 版改变:
Generic
不再拥有自定义元类。
3.12 版改变:
Syntactic support for generics and type aliases is new in version 3.12. Previously, generic classes had to explicitly inherit from
Generic
or contain a type variable in one of their bases.
User-defined generics for parameter expressions are also supported via parameter specification variables in the form
[**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
:
>>> class Z[T, **P]: ... # T is a TypeVar; P is a ParamSpec
...
>>> Z[int, [dict, float]]
__main__.Z[int, [dict, float]]
Classes generic over a
ParamSpec
can also be created using explicit inheritance from
Generic
。在此情况下,
**
is not used:
from typing import ParamSpec, Generic
P = ParamSpec('P')
class Z(Generic[P]):
...
Another difference between
TypeVar
and
ParamSpec
is that 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[**P]: ...
...
>>> X[int, str]
__main__.X[[int, str]]
>>> X[[int, str]]
__main__.X[[int, str]]
Note that generics with
ParamSpec
may not have correct
__parameters__
after substitution in some cases because they are intended primarily for static type checking.
3.10 版改变:
Generic
can now be parameterized over parameter expressions. See
ParamSpec
and
PEP 612
了解更多细节。
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")
使用
object
to indicate that a value could be any type in a typesafe manner. Use
Any
to indicate that a value is dynamically typed.
名义 vs 结构子类型
¶
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
是子类化的
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).
模块内容
¶
The
typing
module defines the following classes, functions and decorators.
特殊 typing 原语
¶
特殊类型
¶
These can be used as types in annotations. They do not support subscription using
[]
.
-
typing.
任何
¶
-
指示无约束类型的特殊类型。
3.11 版改变:
Any
can now be used as a base class. This can be useful for avoiding type checker errors with classes that can duck type anywhere or are highly dynamic.
-
typing.
AnyStr
¶
-
A
constrained type variable
.
定义:
AnyStr = TypeVar('AnyStr', str, bytes)
AnyStr
is meant to be used for functions that may accept
str
or
bytes
arguments but cannot allow the two to mix.
例如:
def concat(a: AnyStr, b: AnyStr) -> AnyStr:
return a + b
concat("foo", "bar") # OK, output has type 'str'
concat(b"foo", b"bar") # OK, output has type 'bytes'
concat("foo", b"bar") # Error, cannot mix str and bytes
Note that, despite its name,
AnyStr
has nothing to do with the
Any
type, nor does it mean “any string”. In particular,
AnyStr
and
str | bytes
are different from each other and have different use cases:
# Invalid use of AnyStr:
# The type variable is used only once in the function signature,
# so cannot be "solved" by the type checker
def greet_bad(cond: bool) -> AnyStr:
return "hi there!" if cond else b"greetings!"
# The better way of annotating this function:
def greet_proper(cond: bool) -> str | bytes:
return "hi there!" if cond else b"greetings!"
Deprecated since version 3.13, will be removed in version 3.18:
Deprecated in favor of the new
type parameter syntax
。使用
class A[T: (str, bytes)]: ...
instead of importing
AnyStr
。见
PEP 695
了解更多细节。
In Python 3.16,
AnyStr
will be removed from
typing.__all__
, and deprecation warnings will be emitted at runtime when it is accessed or imported from
typing
.
AnyStr
will be removed from
typing
in Python 3.18.
-
typing.
LiteralString
¶
-
Special type that includes only literal strings.
Any string literal is compatible with
LiteralString
, as is another
LiteralString
. However, an object typed as just
str
is not. A string created by composing
LiteralString
-typed objects is also acceptable as a
LiteralString
.
范例:
def run_query(sql: LiteralString) -> None:
...
def caller(arbitrary_string: str, literal_string: LiteralString) -> None:
run_query("SELECT * FROM students") # OK
run_query(literal_string) # OK
run_query("SELECT * FROM " + literal_string) # OK
run_query(arbitrary_string) # type checker error
run_query( # type checker error
f"SELECT * FROM students WHERE name = {arbitrary_string}"
)
LiteralString
is useful for sensitive APIs where arbitrary user-generated strings could generate problems. For example, the two cases above that generate type checker errors could be vulnerable to an SQL injection attack.
见
PEP 675
了解更多细节。
Added in version 3.11.
-
typing.
Never
¶
-
typing.
NoReturn
¶
-
Never
and
NoReturn
表示
bottom type
, a type that has no members.
They can be used to indicate that a function never returns, such as
sys.exit()
:
from typing import Never # or NoReturn
def stop() -> Never:
raise RuntimeError('no way')
Or to define a function that should never be called, as there are no valid arguments, such as
assert_never()
:
from typing import Never # or NoReturn
def never_call_me(arg: Never) -> None:
pass
def int_or_str(arg: int | str) -> None:
never_call_me(arg) # type checker error
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
never_call_me(arg) # OK, arg is of type Never (or NoReturn)
Never
and
NoReturn
have the same meaning in the type system and static type checkers treat both equivalently.
Added in version 3.6.2:
添加
NoReturn
.
Added in version 3.11:
添加
Never
.
-
typing.
Self
¶
-
Special type to represent the current enclosed class.
例如:
from typing import Self, reveal_type
class Foo:
def return_self(self) -> Self:
...
return self
class SubclassOfFoo(Foo): pass
reveal_type(Foo().return_self()) # Revealed type is "Foo"
reveal_type(SubclassOfFoo().return_self()) # Revealed type is "SubclassOfFoo"
This annotation is semantically equivalent to the following, albeit in a more succinct fashion:
from typing import TypeVar
Self = TypeVar("Self", bound="Foo")
class Foo:
def return_self(self: Self) -> Self:
...
return self
In general, if something returns
self
, as in the above examples, you should use
Self
as the return annotation. If
Foo.return_self
was annotated as returning
"Foo"
, then the type checker would infer the object returned from
SubclassOfFoo.return_self
as being of type
Foo
而不是
SubclassOfFoo
.
Other common use cases include:
You should not use
Self
as the return annotation if the method is not guaranteed to return an instance of a subclass when the class is subclassed:
class Eggs:
# Self would be an incorrect return annotation here,
# as the object returned is always an instance of Eggs,
# even in subclasses
def returns_eggs(self) -> "Eggs":
return Eggs()
见
PEP 673
了解更多细节。
Added in version 3.11.
-
typing.
TypeAlias
¶
-
Special annotation for explicitly declaring a
类型别名
.
例如:
from typing import TypeAlias
Factors: TypeAlias = list[int]
TypeAlias
is particularly useful on older Python versions for annotating aliases that make use of forward references, as it can be hard for type checkers to distinguish these from normal variable assignments:
from typing import Generic, TypeAlias, TypeVar
T = TypeVar("T")
# "Box" does not exist yet,
# so we have to use quotes for the forward reference on Python <3.12.
# Using ``TypeAlias`` tells the type checker that this is a type alias declaration,
# not a variable assignment to a string.
BoxOfStrings: TypeAlias = "Box[str]"
class Box(Generic[T]):
@classmethod
def make_box_of_strings(cls) -> BoxOfStrings: ...
见
PEP 613
了解更多细节。
3.10 版添加。
Deprecated since version 3.12:
TypeAlias
is deprecated in favor of the
type
statement, which creates instances of
TypeAliasType
and which natively supports forward references. Note that while
TypeAlias
and
TypeAliasType
serve similar purposes and have similar names, they are distinct and the latter is not the type of the former. Removal of
TypeAlias
is not currently planned, but users are encouraged to migrate to
type
语句。