mirror of
https://github.com/python/cpython.git
synced 2025-09-29 03:35:31 +00:00
Expand documentation about type aliases and NewType in the typing module (merge 3.5 -> 3.6).
By Michael Lee.
This commit is contained in:
commit
e608709675
1 changed files with 96 additions and 1 deletions
|
@ -29,10 +29,105 @@ arguments.
|
||||||
Type aliases
|
Type aliases
|
||||||
------------
|
------------
|
||||||
|
|
||||||
A type alias is defined by assigning the type to the alias::
|
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::
|
||||||
|
|
||||||
|
from typing import List
|
||||||
Vector = List[float]
|
Vector = List[float]
|
||||||
|
|
||||||
|
def scale(scalar: float, vector: Vector) -> Vector:
|
||||||
|
return [scalar * num for num in vector]
|
||||||
|
|
||||||
|
# typechecks; 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 typing import Dict, Tuple, List
|
||||||
|
|
||||||
|
ConnectionOptions = Dict[str, str]
|
||||||
|
Address = Tuple[str, int]
|
||||||
|
Server = Tuple[Address, ConnectionOptions]
|
||||||
|
|
||||||
|
def broadcast_message(message: str, servers: List[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: List[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
NewType
|
||||||
|
-------
|
||||||
|
|
||||||
|
Use the ``NewType`` helper function 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:
|
||||||
|
...
|
||||||
|
|
||||||
|
# typechecks
|
||||||
|
user_a = get_user_name(UserId(42351))
|
||||||
|
|
||||||
|
# does not typecheck; 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
|
||||||
|
function that immediately returns whatever parameter you pass it. That means
|
||||||
|
the expression ``Derived(some_value)`` does not create a new class or introduce
|
||||||
|
any overhead beyond that of a regular function call.
|
||||||
|
|
||||||
|
More precisely, the expression ``some_value is Derived(some_value)`` is always
|
||||||
|
true at runtime.
|
||||||
|
|
||||||
|
This also means that it is not possible to create a subtype of ``Derived``
|
||||||
|
since it is an identity function at runtime, not an actual type. Similarly, it
|
||||||
|
is not possible to create another ``NewType`` based on a ``Derived`` type::
|
||||||
|
|
||||||
|
from typing import NewType
|
||||||
|
|
||||||
|
UserId = NewType('UserId', int)
|
||||||
|
|
||||||
|
# Fails at runtime and does not typecheck
|
||||||
|
class AdminUserId(UserId): pass
|
||||||
|
|
||||||
|
# Also does not typecheck
|
||||||
|
ProUserId = NewType('ProUserId', UserId)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
Callable
|
Callable
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue