PEP 526 updates (#84)

This commit is contained in:
Guido van Rossum 2016-09-02 15:03:41 -07:00 committed by GitHub
parent 58c0a72235
commit e48206a7da
1 changed files with 48 additions and 35 deletions

View File

@ -8,7 +8,7 @@ Type: Standards Track
Content-Type: text/x-rst
Created: 09-Aug-2016
Python-Version: 3.6
Post-History: 30-Aug-2016
Post-History: 30-Aug-2016, 02-Sep-2016
Notice for Reviewers
@ -21,7 +21,7 @@ There was preliminary discussion on python-ideas and at
https://github.com/python/typing/issues/258.
Before you bring up an objection in a public forum please at least
read the summary of rejected ideas listed at the end of this PEP.
read the summary of `rejected`_ ideas listed at the end of this PEP.
Abstract
@ -42,8 +42,8 @@ type comments to annotate variables::
stats = {} # type: Dict[str, int]
This PEP aims at adding syntax to Python for annotating the types of variables
(including non-method attributes), instead of expressing them through
comments::
(including class variables and instance variables),
instead of expressing them through comments::
primes: List[int] = []
@ -52,6 +52,12 @@ comments::
class Starship:
stats: ClassVar[Dict[str, int]] = {}
PEP 484 explicitly states that type comments are intended to help with
type inference in complex cases, and this PEP does not change this
intention. However, since in practice type comments have also been
adopted for class variables and instance variables, this PEP also
discusses the use of type annotations for those variables.
Rationale
=========
@ -72,14 +78,14 @@ expressed through comments has some downsides:
my_var = another_function() # Why isn't there a type here?
- Since type comments aren't actually part of the language, if a Python script
wants to parse them, it would require a custom parser instead of just using
wants to parse them, it requires a custom parser instead of just using
``ast``.
- Type comments are used a lot in typeshed. Migrating typeshed to use
the variable annotation syntax instead of type comments would improve
readability of stubs.
- In situations where normal comments and type comments used together, it is
- In situations where normal comments and type comments are used together, it is
difficult to distinguish them::
path = None # type: Optional[str] # Path to module source
@ -97,15 +103,15 @@ by PEP 484.
Non-goals
*********
While the proposal is accompanied by an extension of ``typing.get_type_hints``
standard library function for runtime retrieval of annotations, the variable
While the proposal is accompanied by an extension of the ``typing.get_type_hints``
standard library function for runtime retrieval of annotations, variable
annotations are not designed for runtime type checking. Third party packages
would have to be developed to implement such functionality.
will have to be developed to implement such functionality.
It should also be emphasized that **Python will remain a dynamically typed
language, and the authors have no desire to ever make type hints mandatory,
even by convention.** The goal of annotation syntax is to provide an
easy way to specify the structured type metadata for third party tools.
easy way to specify structured type metadata for third party tools.
This PEP does not require type checkers to change their type checking
rules. It merely provides a more readable syntax to replace type
@ -136,8 +142,8 @@ Global and local variable annotations
The types of locals and globals can be annotated as follows::
some_number: int # variable without default
some_list: List[int] = [] # variable with default
some_number: int # variable without initial value
some_list: List[int] = [] # variable with initial value
Being able to omit the initial value allows for easier typing of variables
assigned in conditional branches::
@ -160,7 +166,7 @@ one to annotate the types of variables when tuple unpacking is used::
body: Optional[List[str]]
header, kind, body = message
Omitting a default value leaves the variable uninitialized::
Omitting the initial value leaves the variable uninitialized::
a: int
print(a) # raises NameError
@ -201,50 +207,57 @@ in ``__init__`` or ``__new__``. The proposed syntax is as follows::
damage: int # instance variable without default
stats: ClassVar[Dict[str, int]] = {} # class variable
Here ``ClassVar`` is a special class in typing module that indicates to
static type checker that this variable should not be set on instances.
Here ``ClassVar`` is a special class defined by the typing module that
indicates to the static type checker that this variable should not be
set on instances.
This could be illustrated with a more detailed example. In this class::
class Starship:
captain = 'Picard'
stats = {}
def __init__(self, damage, captain=None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
def hit(self):
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
``stats`` is intended to be a class variable (keeping track of many different
per-game statistics), while ``captain`` is an instance variable with a default
value set in the class. This difference could not be seen by type
checker -- both get initialized in the class, but ``captain`` serves only
value set in the class. This difference might not be seen by a type
checker: both get initialized in the class, but ``captain`` serves only
as a convenient default value for the instance variable, while ``stats``
is truly a class variable -- it is intended to be shared by all instances.
Since both variables happen to be initialized at the class level, it is
useful to distinguish them by marking class variables as annotated with
types wrapped in ``ClassVar[...]``. In such way type checker will prevent
accidental assignments to attributes with a same name on instances.
types wrapped in ``ClassVar[...]``. In this way a type checker may flag
accidental assignments to attributes with the same name on instances.
For example, annotating the discussed class::
class Starship:
captain: str = 'Picard'
damage: int
stats: ClassVar[Dict[str, int]] = {}
def __init__(self, damage: int, captain: str = None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
def hit(self):
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
enterprise_d = Starship(3000)
enterprise_d.stats = {} # Flagged as error by a type checker
Starship.stats = {} # This is OK
enterprise_d = Starship(3000)
enterprise_d.stats = {} # Flagged as error by a type checker
Starship.stats = {} # This is OK
As a matter of convenience, instance variables can be annotated in
``__init__`` or other methods, rather than in class::
As a matter of convenience (and convention), instance variables can be
annotated in ``__init__`` or other methods, rather than in the class::
from typing import Generic, TypeVar
T = TypeVar(T)
@ -256,7 +269,9 @@ As a matter of convenience, instance variables can be annotated in
Annotating expressions
**********************
The target of the annotation can be any valid single assignment target::
The target of the annotation can be any valid single assignment
target, at least syntactically (it is up to the type checker what to
do with this)::
class Cls:
pass
@ -269,7 +284,7 @@ The target of the annotation can be any valid single assignment target::
d['a']: int = 0 # Annotates d['a'] with int.
d['b']: int # Annotates d['b'] with int.
Note that even a parenthesized name considered an expression,
Note that even a parenthesized name is considered an expression,
not a simple name::
(x): int # Annotates x with int, (x) treated as expression by compiler.
@ -310,7 +325,7 @@ Changes to Standard Library and Documentation
- A new covariant type ``ClassVar[T_co]`` is added to the ``typing``
module. It accepts only a single argument that should be a valid type,
and is used to annotate class variables that should no be set on class
and is used to annotate class variables that should not be set on class
instances. This restriction is ensured by static checkers,
but not at runtime. See the
`classvar`_ section for examples and explanations for the usage of
@ -319,7 +334,7 @@ Changes to Standard Library and Documentation
- Function ``get_type_hints`` in the ``typing`` module will be extended,
so that one can retrieve type annotations at runtime from modules
and classes in addition to functions.
and classes as well as functions.
Annotations are returned as a dictionary mapping from variable or arguments
to their type hints with forward references evaluated.
For classes it returns a mapping (perhaps ``collections.ChainMap``)
@ -460,8 +475,7 @@ Rejected/Postponed Proposals
course subjective.)
- **Allow type annotations for tuple unpacking:**
This causes an ambiguity: It's not clear what meaning should be
assigned to this statement::
This causes ambiguity: it's not clear what this statement means::
x, y: T
@ -484,8 +498,8 @@ Rejected/Postponed Proposals
x: int = y = 1
z = w: int = 1
it is ambiguous, what should be the type of ``y``, and what should
be the type of ``z``. Also the second line is difficult to parse.
it is ambiguous, what should the types of ``y`` and ``z`` be?
Also the second line is difficult to parse.
- **Allow annotations in ``with`` and ``for`` statement:**
This was rejected because in ``for`` it would make it hard to spot the actual
@ -523,9 +537,8 @@ Rejected/Postponed Proposals
- **Use syntax** ``x: class t = v`` **for class variables:**
This would require a more complicated parser and the ``class``
keyword would confuse simple-minded syntax highlighters. Anyway we
need to have ``ClassVar`` to store class variables to
``__annotations__``, so that it was decided to go with a simpler
syntax.
need to have ``ClassVar`` store class variables to
``__annotations__``, so a simpler syntax was chosen.
- **Forget about** ``ClassVar`` **altogether:**
This was proposed since mypy seems to be getting along fine without a way