Manually merge changes to PEP 526.

This commit is contained in:
Guido van Rossum 2016-09-02 11:45:59 -07:00
parent 9d7598135d
commit 58c0a72235
1 changed files with 75 additions and 63 deletions

View File

@ -1,5 +1,5 @@
PEP: 526 PEP: 526
Title: Syntax for Variable and Attribute Annotations Title: Syntax for Variable Annotations
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Ryan Gonzalez <rymg19@gmail.com>, Philip House <phouse512@gmail.com>, Ivan Levkivskyi <levkivskyi@gmail.com>, Lisa Roach <lisaroach14@gmail.com>, Guido van Rossum <guido@python.org> Author: Ryan Gonzalez <rymg19@gmail.com>, Philip House <phouse512@gmail.com>, Ivan Levkivskyi <levkivskyi@gmail.com>, Lisa Roach <lisaroach14@gmail.com>, Guido van Rossum <guido@python.org>
@ -38,11 +38,12 @@ type comments to annotate variables::
captain = ... # type: str captain = ... # type: str
class Starship: class Starship:
# 'stats' is a class attribute # 'stats' is a class variable
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
and attributes, instead of expressing them through comments:: (including non-method attributes), instead of expressing them through
comments::
primes: List[int] = [] primes: List[int] = []
@ -60,7 +61,7 @@ expressed through comments has some downsides:
- Text editors often highlight comments differently from type annotations. - Text editors often highlight comments differently from type annotations.
- There's no way to annotate the type of an undefined variable; you need to - There's no way to annotate the type of an undefined variable; one needs to
initialize it to ``None`` (e.g. ``a = None # type: int``). initialize it to ``None`` (e.g. ``a = None # type: int``).
- Variables annotated in a conditional branch are difficult to read:: - Variables annotated in a conditional branch are difficult to read::
@ -88,7 +89,10 @@ expressed through comments has some downsides:
which is inelegant, to say the least. which is inelegant, to say the least.
The majority of these issues can be alleviated by making the syntax The majority of these issues can be alleviated by making the syntax
a core part of the language. a core part of the language. Moreover, having a dedicated annotation syntax
for class and instance variables (in addition to method annotations) will
pave the way to static duck-typing as a complement to nominal typing defined
by PEP 484.
Non-goals Non-goals
********* *********
@ -103,12 +107,16 @@ 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 the 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
comments.
Specification Specification
============= =============
Type annotation can be added to an assignment statement or to a simple Type annotation can be added to an assignment statement or to a single
name indicating the desired type of the annotation target to a third expression indicating the desired type of the annotation target to a third
party type checker:: party type checker::
my_var: int my_var: int
@ -116,11 +124,15 @@ party type checker::
other_var: int = 'a' # Flagged as error by type checker, other_var: int = 'a' # Flagged as error by type checker,
# but OK at runtime. # but OK at runtime.
Below we specify the semantics of type annotations for type checkers Below we specify the syntax of type annotations
in different contexts and their runtime effects. in different contexts and their runtime effects.
Variable Annotations We also suggest how type checkers might interpret annotations, but
******************** compliance to these suggestions is not mandatory. (This is in line
with the attitude towards compliance in PEP 484.)
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::
@ -159,7 +171,7 @@ it a local::
def f(): def f():
a: int a: int
print(a) # raises UnboundLocalError print(a) # raises UnboundLocalError
# Commenting out the ``a: int`` makes it a NameError. # Commenting out the a: int makes it a NameError.
as if the code were:: as if the code were::
@ -168,44 +180,29 @@ as if the code were::
print(a) # raises UnboundLocalError print(a) # raises UnboundLocalError
Duplicate type annotations will be ignored. However, static type Duplicate type annotations will be ignored. However, static type
checkers will issue a warning for annotations of the same variable checkers may issue a warning for annotations of the same variable
by a different type:: by a different type::
a: int a: int
a: str # Static type checker will warn about this. a: str # Static type checker may or may not warn about this.
``__annotations__`` is writable, so this is permitted:: .. _classvar:
__annotations__['s'] = str Class and instance variable annotations
***************************************
But attempting to update ``__annotations__`` to something other than a dict Type annotations can also be used to annotate class and instance variables
may result in a TypeError:: in class bodies and methods. In particular, the value-less notation ``a: int``
allows one to annotate instance variables that should be initialized
class C: in ``__init__`` or ``__new__``. The proposed syntax is as follows::
__annotations__ = 42
x: int = 5 # raises TypeError
(Note that the assignment to ``__annotations__``, which is the
culprit, is accepted by the Python interpreter without questioning it
-- but the subsequent type annotation expects it to be a
``MutableMapping`` and will fail.)
Attribute annotations
*********************
Type annotations can also be used to annotate attributes
in class bodies. In particular, the value-less notation ``a: int`` allows us
to annotate instance variables that should be initialized in ``__init__``
or ``__new__``. The proposed syntax is as follows::
class BasicStarship: class BasicStarship:
captain: str = 'Picard' # instance variable with default captain: str = 'Picard' # instance variable with default
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 in typing module that indicates to
static type checker that this attribute should not be set on class instances. 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:
@ -228,7 +225,7 @@ 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 such way type checker will prevent
accidental assignments to attributes with a same name on class instances. accidental assignments to attributes with a same name on instances.
For example, annotating the discussed class:: For example, annotating the discussed class::
class Starship: class Starship:
@ -246,7 +243,7 @@ For example, annotating the discussed class::
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 attributes can be annotated in As a matter of convenience, instance variables can be annotated in
``__init__`` or other methods, rather than in class:: ``__init__`` or other methods, rather than in class::
from typing import Generic, TypeVar from typing import Generic, TypeVar
@ -259,27 +256,24 @@ As a matter of convenience, instance attributes can be annotated in
Annotating expressions Annotating expressions
********************** **********************
If the initial value is specified, then the target of the annotation can be The target of the annotation can be any valid single assignment target::
any valid single assignment target::
class Cls: class Cls:
pass pass
c = Cls() c = Cls()
c.x: int = 0 # Annotates c.x with int. c.x: int = 0 # Annotates c.x with int.
c.y: int # Invalid syntax: no initial value was specified! c.y: int # Annotates c.y with int.
d = {} d = {}
d['a']: int = 0 # Annotates d['a'] with int. d['a']: int = 0 # Annotates d['a'] with int.
d['b']: int # Invalid again. d['b']: int # Annotates d['b'] with int.
Note that even ``(my_var)`` is considered an expression, not a simple name. Note that even a parenthesized name considered an expression,
Consequently:: not a simple name::
(x): int # Invalid syntax (x): int # Annotates x with int, (x) treated as expression by compiler.
(x): int = 0 # OK (y): int = 0 # Same situation here.
It is up to the type checker to decide exactly when to accept this syntax.
Where annotations aren't allowed Where annotations aren't allowed
******************************** ********************************
@ -311,22 +305,23 @@ unpacking::
... ...
Changes to standard library and documentation 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 no 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 Attribute Annotations for examples but not at runtime. See the
and explanations for the usage of ``ClassVar``, and see the Rejected `classvar`_ section for examples and explanations for the usage of
Proposals section for more information on the reasoning behind ``ClassVar``. ``ClassVar``, and see the `rejected`_ section for more information on
the reasoning behind ``ClassVar``.
- 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 in addition to functions.
Annotations are returned as a dictionary mapping from variable, arguments, Annotations are returned as a dictionary mapping from variable or arguments
or attributes 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``)
constructed from annotations in method resolution order. constructed from annotations in method resolution order.
@ -337,7 +332,7 @@ Changes to standard library and documentation
separately from the standard library. separately from the standard library.
Runtime effects of type annotations Runtime Effects of Type Annotations
=================================== ===================================
Annotating a local variable will cause Annotating a local variable will cause
@ -352,10 +347,10 @@ evaluated::
x: NonexistentName # Error! x: NonexistentName # Error!
class X: class X:
attr: NonexistentName # Error! var: NonexistentName # Error!
In addition, at the module or class level, if the item being annotated is a In addition, at the module or class level, if the item being annotated is a
simple name, then it and the annotation will be stored in the *simple name*, then it and the annotation will be stored in the
``__annotations__`` attribute of that module or class as a dictionary mapping ``__annotations__`` attribute of that module or class as a dictionary mapping
from names to evaluated annotations. Here is an example:: from names to evaluated annotations. Here is an example::
@ -367,6 +362,22 @@ from names to evaluated annotations. Here is an example::
print(__annotations__) print(__annotations__)
# prints: {'players': typing.Dict[str, __main__.Player]} # prints: {'players': typing.Dict[str, __main__.Player]}
``__annotations__`` is writable, so this is permitted::
__annotations__['s'] = str
But attempting to update ``__annotations__`` to something other than a dict
may result in a TypeError::
class C:
__annotations__ = 42
x: int = 5 # raises TypeError
(Note that the assignment to ``__annotations__``, which is the
culprit, is accepted by the Python interpreter without questioning it
-- but the subsequent type annotation expects it to be a
``MutableMapping`` and will fail.)
The recommended way of getting annotations at runtime is by using The recommended way of getting annotations at runtime is by using
``typing.get_type_hints`` function; as with all dunder attributes, ``typing.get_type_hints`` function; as with all dunder attributes,
any undocummented use of ``__annotations__`` is subject to breakage any undocummented use of ``__annotations__`` is subject to breakage
@ -416,9 +427,10 @@ These stored annotations might be used for other purposes,
but with this PEP we explicitly recommend type hinting as the but with this PEP we explicitly recommend type hinting as the
preferred use of annotations. preferred use of annotations.
.. _rejected:
Rejected proposals and things left out for now Rejected/Postponed Proposals
============================================== ============================
- **Should we introduce variable annotations at all?** - **Should we introduce variable annotations at all?**
Variable annotations have *already* been around for almost two years Variable annotations have *already* been around for almost two years
@ -495,11 +507,11 @@ Rejected proposals and things left out for now
would needed to be checked for everywhere in the code. Therefore, would needed to be checked for everywhere in the code. Therefore,
Guido just said plain "No" to this. Guido just said plain "No" to this.
- **Add also** ``InstanceAttr`` **to the typing module:** - **Add also** ``InstanceVar`` **to the typing module:**
This is redundant because instance variables are way more common than This is redundant because instance variables are way more common than
class variables. The more common usage deserves to be the default. class variables. The more common usage deserves to be the default.
- **Allow instance attribute annotations only in methods:** - **Allow instance variable annotations only in methods:**
The problem is that many ``__init__`` methods do a lot of things besides The problem is that many ``__init__`` methods do a lot of things besides
initializing instance variables, and it would be harder (for a human) initializing instance variables, and it would be harder (for a human)
to find all the instance variable declarations. to find all the instance variable declarations.
@ -529,7 +541,7 @@ Rejected proposals and things left out for now
are always evaluated. Although this might be reconsidered in future, are always evaluated. Although this might be reconsidered in future,
it was decided in PEP 484 that this would have to be a separate PEP. it was decided in PEP 484 that this would have to be a separate PEP.
- **Declare attribute types in class docstring:** - **Declare variable types in class docstring:**
Many projects already use various docstring conventions, often without Many projects already use various docstring conventions, often without
much consistency and generally without conforming to the PEP 484 annotation much consistency and generally without conforming to the PEP 484 annotation
syntax yet. Also this would require a special sophisticated parser. syntax yet. Also this would require a special sophisticated parser.