Skip to content

Commit c85a962

Browse files
author
Guido van Rossum
committed
Update the section on type erasure to disallow Node[int]() -- you must use a type alias. Fixes #79.
1 parent 7b43ada commit c85a962

File tree

1 file changed

+46
-29
lines changed

1 file changed

+46
-29
lines changed

pep-0484.txt

+46-29
Original file line numberDiff line numberDiff line change
@@ -462,41 +462,58 @@ Suppose we write a ``Node`` class inheriting from ``Generic[T]``::
462462
class Node(Generic[T]):
463463
...
464464

465-
Now there are two ways we can instantiate this class; the type
466-
inferred by a type checker may be different depending on the form we
467-
use. The first way is to give the value of the type parameter
468-
explicitly -- this overrides whatever type inference the type
469-
checker would otherwise perform::
465+
To create ``Node`` instances you call ``Node()`` just as for a regular
466+
class. At runtime the type (class) of the instance will be ``Node``.
467+
But what type does it have to the type checker? The answer depends on
468+
how much information is available in the call. If the constructor
469+
(``__init__`` or ``__new__``) uses ``T`` in its signature, and a
470+
corresponding argument value is passed, the type of the corresponding
471+
argument(s) is substituted. Otherwise, ``Any`` is assumed. Example::
470472

471-
x = Node[T]() # The type inferred for x is Node[T].
472-
473-
y = Node[int]() # The type inferred for y is Node[int].
474-
475-
If no explicit types are given, the type checker is given some
476-
freedom. Consider this code::
477-
478-
x = Node()
479-
480-
The inferred type could be ``Node[Any]``, as there isn't enough
481-
context to infer a more precise type. Alternatively, a type checker
482-
may reject the line and require an explicit annotation, like this::
483-
484-
x = Node() # type: Node[int] # Inferred type is Node[int].
473+
from typing import TypeVar, Generic
485474

486-
A type checker with more powerful type inference could look at how
487-
``x`` is used elsewhere in the file and try to infer a more precise
488-
type such as ``Node[int]`` even without an explicit type annotation.
489-
However, it is probably impossible to make such type inference work
490-
well in all cases, since Python programs can be very dynamic.
475+
T = TypeVar('T')
491476

492-
This PEP doesn't specify the details of how type inference should
493-
work. We allow different tools to experiment with various approaches.
494-
We may give more explicit rules in future revisions.
477+
class Node(Generic[T]):
478+
def __init__(self, label: T = None) -> None:
479+
...
495480

496-
At runtime the type is not preserved, and the class of ``x`` is just
497-
``Node`` in all cases. This behavior is called "type erasure"; it is
481+
x = Node('') # Inferred type is Node[str]
482+
y = Node(0) # Inferred type is Node[int]
483+
z = Node() # Inferred type is Node[Any]
484+
485+
In case the inferred type uses ``[Any]`` but the intended type is more
486+
specific, you can use a type comment (see below) to force the type of
487+
the variable, e.g.::
488+
489+
# (continued from previous example)
490+
a = Node() # type: Node[int]
491+
b = Node() # type: Node[str]
492+
493+
You can also create a type alias (see above) for a specific concrete
494+
type and instantiate it, e.g.::
495+
496+
# (continued from previous example)
497+
IntNode = Node[int]
498+
StrNode = Node[str]
499+
p = IntNode() # Inferred type is Node[str]
500+
q = StrNode() # Inferred type is Node[int]
501+
r = IntNode('') # Error
502+
s = StrNode(0) # Error
503+
504+
Note that the runtime type (class) of p and q is still just ``Node``
505+
-- ``IntNode`` and ``StrNode`` are distinguishable class objects, but
506+
the type (class) of the objects created by instantiating them doesn't
507+
record the distinction. This behavior is called "type erasure"; it is
498508
common practice in languages with generics (e.g. Java, TypeScript).
499509

510+
You cannot use the subscripted class (e.g. ``Node[int]``) directly in
511+
an expression -- you must define a type alias. (This restriction
512+
exists because creating the subscripted class, e.g. ``Node[int]``, is
513+
an expensive operation -- usually many times as expensive as
514+
constructing an instance of it. Using a type alias is also more
515+
readable.)
516+
500517

501518
Arbitrary generic types as base classes
502519
---------------------------------------

0 commit comments

Comments
 (0)