Update the section on type erasure to disallow Node[int]() -- you must use a type alias.

This commit is contained in:
Guido van Rossum 2016-04-04 17:39:26 -07:00
parent b31d3f0ea7
commit 9f33fa7793
1 changed files with 41 additions and 24 deletions

View File

@ -462,41 +462,58 @@ Suppose we write a ``Node`` class inheriting from ``Generic[T]``::
class Node(Generic[T]): class Node(Generic[T]):
... ...
Now there are two ways we can instantiate this class; the type To create ``Node`` instances you call ``Node()`` just as for a regular
inferred by a type checker may be different depending on the form we class. At runtime the type (class) of the instance will be ``Node``.
use. The first way is to give the value of the type parameter But what type does it have to the type checker? The answer depends on
explicitly -- this overrides whatever type inference the type how much information is available in the call. If the constructor
checker would otherwise perform:: (``__init__`` or ``__new__``) uses ``T`` in its signature, and a
corresponding argument value is passed, the type of the corresponding
argument(s) is substituted. Otherwise, ``Any`` is assumed. Example::
x = Node[T]() # The type inferred for x is Node[T]. from typing import TypeVar, Generic
y = Node[int]() # The type inferred for y is Node[int]. T = TypeVar('T')
If no explicit types are given, the type checker is given some class Node(Generic[T]):
freedom. Consider this code:: def __init__(self, label: T = None) -> None:
...
x = Node() x = Node('') # Inferred type is Node[str]
y = Node(0) # Inferred type is Node[int]
z = Node() # Inferred type is Node[Any]
The inferred type could be ``Node[Any]``, as there isn't enough In case the inferred type uses ``[Any]`` but the intended type is more
context to infer a more precise type. Alternatively, a type checker specific, you can use a type comment (see below) to force the type of
may reject the line and require an explicit annotation, like this:: the variable, e.g.::
x = Node() # type: Node[int] # Inferred type is Node[int]. # (continued from previous example)
a = Node() # type: Node[int]
b = Node() # type: Node[str]
A type checker with more powerful type inference could look at how You can also create a type alias (see above) for a specific concrete
``x`` is used elsewhere in the file and try to infer a more precise type and instantiate it, e.g.::
type such as ``Node[int]`` even without an explicit type annotation.
However, it is probably impossible to make such type inference work
well in all cases, since Python programs can be very dynamic.
This PEP doesn't specify the details of how type inference should # (continued from previous example)
work. We allow different tools to experiment with various approaches. IntNode = Node[int]
We may give more explicit rules in future revisions. StrNode = Node[str]
p = IntNode() # Inferred type is Node[str]
q = StrNode() # Inferred type is Node[int]
r = IntNode('') # Error
s = StrNode(0) # Error
At runtime the type is not preserved, and the class of ``x`` is just Note that the runtime type (class) of p and q is still just ``Node``
``Node`` in all cases. This behavior is called "type erasure"; it is -- ``IntNode`` and ``StrNode`` are distinguishable class objects, but
the type (class) of the objects created by instantiating them doesn't
record the distinction. This behavior is called "type erasure"; it is
common practice in languages with generics (e.g. Java, TypeScript). common practice in languages with generics (e.g. Java, TypeScript).
You cannot use the subscripted class (e.g. ``Node[int]``) directly in
an expression -- you must define a type alias. (This restriction
exists because creating the subscripted class, e.g. ``Node[int]``, is
an expensive operation -- usually many times as expensive as
constructing an instance of it. Using a type alias is also more
readable.)
Arbitrary generic types as base classes Arbitrary generic types as base classes
--------------------------------------- ---------------------------------------