diff -r da9898e7e90d -r 3ae1b33f6cd8 Doc/library/typing.rst --- a/Doc/library/typing.rst Wed Jul 27 16:59:22 2016 +0200 +++ b/Doc/library/typing.rst Thu Jul 28 10:12:22 2016 -0700 @@ -29,10 +29,105 @@ 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] + 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 --------