Update the section on type erasure to disallow Node[int]() -- you must use a type alias.
This commit is contained in:
parent
b31d3f0ea7
commit
9f33fa7793
65
pep-0484.txt
65
pep-0484.txt
|
@ -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
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue