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
|
Content-Type: text/x-rst
|
||||||
Created: 09-Aug-2016
|
Created: 09-Aug-2016
|
||||||
Python-Version: 3.6
|
Python-Version: 3.6
|
||||||
Post-History: 30-Aug-2016
|
Post-History: 30-Aug-2016, 02-Sep-2016
|
||||||
|
|
||||||
|
|
||||||
Notice for Reviewers
|
Notice for Reviewers
|
||||||
|
@ -21,7 +21,7 @@ There was preliminary discussion on python-ideas and at
|
||||||
https://github.com/python/typing/issues/258.
|
https://github.com/python/typing/issues/258.
|
||||||
|
|
||||||
Before you bring up an objection in a public forum please at least
|
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
|
Abstract
|
||||||
|
@ -42,8 +42,8 @@ type comments to annotate variables::
|
||||||
stats = {} # type: Dict[str, int]
|
stats = {} # type: Dict[str, int]
|
||||||
|
|
||||||
This PEP aims at adding syntax to Python for annotating the types of variables
|
This PEP aims at adding syntax to Python for annotating the types of variables
|
||||||
(including non-method attributes), instead of expressing them through
|
(including class variables and instance variables),
|
||||||
comments::
|
instead of expressing them through comments::
|
||||||
|
|
||||||
primes: List[int] = []
|
primes: List[int] = []
|
||||||
|
|
||||||
|
@ -52,6 +52,12 @@ comments::
|
||||||
class Starship:
|
class Starship:
|
||||||
stats: ClassVar[Dict[str, int]] = {}
|
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
|
Rationale
|
||||||
=========
|
=========
|
||||||
|
@ -72,14 +78,14 @@ expressed through comments has some downsides:
|
||||||
my_var = another_function() # Why isn't there a type here?
|
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
|
- 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``.
|
``ast``.
|
||||||
|
|
||||||
- Type comments are used a lot in typeshed. Migrating typeshed to use
|
- Type comments are used a lot in typeshed. Migrating typeshed to use
|
||||||
the variable annotation syntax instead of type comments would improve
|
the variable annotation syntax instead of type comments would improve
|
||||||
readability of stubs.
|
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::
|
difficult to distinguish them::
|
||||||
|
|
||||||
path = None # type: Optional[str] # Path to module source
|
path = None # type: Optional[str] # Path to module source
|
||||||
|
@ -97,15 +103,15 @@ by PEP 484.
|
||||||
Non-goals
|
Non-goals
|
||||||
*********
|
*********
|
||||||
|
|
||||||
While the proposal is accompanied by an extension of ``typing.get_type_hints``
|
While the proposal is accompanied by an extension of the ``typing.get_type_hints``
|
||||||
standard library function for runtime retrieval of annotations, the variable
|
standard library function for runtime retrieval of annotations, variable
|
||||||
annotations are not designed for runtime type checking. Third party packages
|
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
|
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,
|
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
|
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
|
This PEP does not require type checkers to change their type checking
|
||||||
rules. It merely provides a more readable syntax to replace type
|
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::
|
The types of locals and globals can be annotated as follows::
|
||||||
|
|
||||||
some_number: int # variable without default
|
some_number: int # variable without initial value
|
||||||
some_list: List[int] = [] # variable with default
|
some_list: List[int] = [] # variable with initial value
|
||||||
|
|
||||||
Being able to omit the initial value allows for easier typing of variables
|
Being able to omit the initial value allows for easier typing of variables
|
||||||
assigned in conditional branches::
|
assigned in conditional branches::
|
||||||
|
@ -160,7 +166,7 @@ one to annotate the types of variables when tuple unpacking is used::
|
||||||
body: Optional[List[str]]
|
body: Optional[List[str]]
|
||||||
header, kind, body = message
|
header, kind, body = message
|
||||||
|
|
||||||
Omitting a default value leaves the variable uninitialized::
|
Omitting the initial value leaves the variable uninitialized::
|
||||||
|
|
||||||
a: int
|
a: int
|
||||||
print(a) # raises NameError
|
print(a) # raises NameError
|
||||||
|
@ -201,50 +207,57 @@ in ``__init__`` or ``__new__``. The proposed syntax is as follows::
|
||||||
damage: int # instance variable without default
|
damage: int # instance variable without default
|
||||||
stats: ClassVar[Dict[str, int]] = {} # class variable
|
stats: ClassVar[Dict[str, int]] = {} # class variable
|
||||||
|
|
||||||
Here ``ClassVar`` is a special class in typing module that indicates to
|
Here ``ClassVar`` is a special class defined by the typing module that
|
||||||
static type checker that this variable should not be set on instances.
|
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::
|
This could be illustrated with a more detailed example. In this class::
|
||||||
|
|
||||||
class Starship:
|
class Starship:
|
||||||
captain = 'Picard'
|
captain = 'Picard'
|
||||||
stats = {}
|
stats = {}
|
||||||
|
|
||||||
def __init__(self, damage, captain=None):
|
def __init__(self, damage, captain=None):
|
||||||
self.damage = damage
|
self.damage = damage
|
||||||
if captain:
|
if captain:
|
||||||
self.captain = captain # Else keep the default
|
self.captain = captain # Else keep the default
|
||||||
|
|
||||||
def hit(self):
|
def hit(self):
|
||||||
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
|
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
|
||||||
|
|
||||||
``stats`` is intended to be a class variable (keeping track of many different
|
``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
|
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
|
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
|
checker: both get initialized in the class, but ``captain`` serves only
|
||||||
as a convenient default value for the instance variable, while ``stats``
|
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.
|
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
|
Since both variables happen to be initialized at the class level, it is
|
||||||
useful to distinguish them by marking class variables as annotated with
|
useful to distinguish them by marking class variables as annotated with
|
||||||
types wrapped in ``ClassVar[...]``. In such way type checker will prevent
|
types wrapped in ``ClassVar[...]``. In this way a type checker may flag
|
||||||
accidental assignments to attributes with a same name on instances.
|
accidental assignments to attributes with the same name on instances.
|
||||||
|
|
||||||
For example, annotating the discussed class::
|
For example, annotating the discussed class::
|
||||||
|
|
||||||
class Starship:
|
class Starship:
|
||||||
captain: str = 'Picard'
|
captain: str = 'Picard'
|
||||||
damage: int
|
damage: int
|
||||||
stats: ClassVar[Dict[str, int]] = {}
|
stats: ClassVar[Dict[str, int]] = {}
|
||||||
|
|
||||||
def __init__(self, damage: int, captain: str = None):
|
def __init__(self, damage: int, captain: str = None):
|
||||||
self.damage = damage
|
self.damage = damage
|
||||||
if captain:
|
if captain:
|
||||||
self.captain = captain # Else keep the default
|
self.captain = captain # Else keep the default
|
||||||
|
|
||||||
def hit(self):
|
def hit(self):
|
||||||
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
|
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
|
||||||
|
|
||||||
enterprise_d = Starship(3000)
|
enterprise_d = Starship(3000)
|
||||||
enterprise_d.stats = {} # Flagged as error by a type checker
|
enterprise_d.stats = {} # Flagged as error by a type checker
|
||||||
Starship.stats = {} # This is OK
|
Starship.stats = {} # This is OK
|
||||||
|
|
||||||
As a matter of convenience, instance variables can be annotated in
|
As a matter of convenience (and convention), instance variables can be
|
||||||
``__init__`` or other methods, rather than in class::
|
annotated in ``__init__`` or other methods, rather than in the class::
|
||||||
|
|
||||||
from typing import Generic, TypeVar
|
from typing import Generic, TypeVar
|
||||||
T = TypeVar(’T’)
|
T = TypeVar(’T’)
|
||||||
|
@ -256,7 +269,9 @@ As a matter of convenience, instance variables can be annotated in
|
||||||
Annotating expressions
|
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:
|
class Cls:
|
||||||
pass
|
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['a']: int = 0 # Annotates d['a'] with int.
|
||||||
d['b']: int # Annotates d['b'] 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::
|
not a simple name::
|
||||||
|
|
||||||
(x): int # Annotates x with int, (x) treated as expression by compiler.
|
(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``
|
- 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,
|
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,
|
instances. This restriction is ensured by static checkers,
|
||||||
but not at runtime. See the
|
but not at runtime. See the
|
||||||
`classvar`_ section for examples and explanations for the usage of
|
`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,
|
- Function ``get_type_hints`` in the ``typing`` module will be extended,
|
||||||
so that one can retrieve type annotations at runtime from modules
|
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
|
Annotations are returned as a dictionary mapping from variable or arguments
|
||||||
to their type hints with forward references evaluated.
|
to their type hints with forward references evaluated.
|
||||||
For classes it returns a mapping (perhaps ``collections.ChainMap``)
|
For classes it returns a mapping (perhaps ``collections.ChainMap``)
|
||||||
|
@ -460,8 +475,7 @@ Rejected/Postponed Proposals
|
||||||
course subjective.)
|
course subjective.)
|
||||||
|
|
||||||
- **Allow type annotations for tuple unpacking:**
|
- **Allow type annotations for tuple unpacking:**
|
||||||
This causes an ambiguity: It's not clear what meaning should be
|
This causes ambiguity: it's not clear what this statement means::
|
||||||
assigned to this statement::
|
|
||||||
|
|
||||||
x, y: T
|
x, y: T
|
||||||
|
|
||||||
|
@ -484,8 +498,8 @@ Rejected/Postponed Proposals
|
||||||
x: int = y = 1
|
x: int = y = 1
|
||||||
z = w: int = 1
|
z = w: int = 1
|
||||||
|
|
||||||
it is ambiguous, what should be the type of ``y``, and what should
|
it is ambiguous, what should the types of ``y`` and ``z`` be?
|
||||||
be the type of ``z``. Also the second line is difficult to parse.
|
Also the second line is difficult to parse.
|
||||||
|
|
||||||
- **Allow annotations in ``with`` and ``for`` statement:**
|
- **Allow annotations in ``with`` and ``for`` statement:**
|
||||||
This was rejected because in ``for`` it would make it hard to spot the actual
|
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:**
|
- **Use syntax** ``x: class t = v`` **for class variables:**
|
||||||
This would require a more complicated parser and the ``class``
|
This would require a more complicated parser and the ``class``
|
||||||
keyword would confuse simple-minded syntax highlighters. Anyway we
|
keyword would confuse simple-minded syntax highlighters. Anyway we
|
||||||
need to have ``ClassVar`` to store class variables to
|
need to have ``ClassVar`` store class variables to
|
||||||
``__annotations__``, so that it was decided to go with a simpler
|
``__annotations__``, so a simpler syntax was chosen.
|
||||||
syntax.
|
|
||||||
|
|
||||||
- **Forget about** ``ClassVar`` **altogether:**
|
- **Forget about** ``ClassVar`` **altogether:**
|
||||||
This was proposed since mypy seems to be getting along fine without a way
|
This was proposed since mypy seems to be getting along fine without a way
|
||||||
|
|
Loading…
Reference in New Issue