Manually merge changes to PEP 526.
This commit is contained in:
parent
9d7598135d
commit
58c0a72235
138
pep-0526.txt
138
pep-0526.txt
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue