PEP 526 updates (#84)
This commit is contained in:
parent
58c0a72235
commit
e48206a7da
83
pep-0526.txt
83
pep-0526.txt
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue