python-peps/pep-0526.txt

222 lines
6.4 KiB
Plaintext

PEP: 526
Title: Syntax for Variable and Attribute Annotations
Version: $Revision$
Last-Modified: $Date$
Author: Ryan Gonzalez <rymg19@gmail.com>, Philip House <phouse512@gmail.com>, Guido van Rossum <guido@python.org>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 09-Aug-2016
Python-Version: 3.6
Notice for Reviewers
====================
This PEP is not ready for review. We're merely committing changes
frequently so we don't end up with a huge merge conflict. For minor
textual nits please use https://github.com/python/peps/pull/72. For
discussion about contents, please refer to
https://github.com/python/typing/issues/258 (but please be patient, we
know we're way behind addressing all comments).
Abstract
========
PEP 484 introduced type hints and; In particular, it introduced the notion of
type comments::
# a is specified to be a list of ints.
a = [] # type: List[int]
# b is a string
b = None # type: str
class Cls:
my_class_attr = True # type: bool
This PEP aims at adding syntax to Python for annotating the types of variables and
attributes, instead of expressing them through comments::
a: List[int] = []
b: str
class Cls:
my_class_attr: ClassAttr[bool] = True
Rationale
=========
Although type comments work well, the fact that they're expressed through
comments has some downsides:
- Text editors often highlight comments differently from type annotations.
- There isn't a way to annotate the type of an undefined variable; you need to
initialize it to ``None`` (e.g. ``a = None # type: int``).
- Variables annotated in a conditional branch are difficult to read::
if some_value:
my_var = function() # type: Logger
else:
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
``ast``.
- It's impossible to retrieve the annotations at runtime outside of attempting to
find the module's source code and parse it at runtime, which is inelegant, to
say the least.
The majority of these issues can be alleviated by making the syntax a core part of
the language.
Specification
=============
*** big key concepts, not quite sure what the best way to organize this would be,
or if they deserve their own sections ***
Normal Variable Typing
**********************
The types of locals and globals can be annotated as follows::
some_number: int # variable without default
some_list: List[int] = [] # variable with default
Being able to omit the initial value allows for easier typing of variables
assigned in conditional branches::
sane_world: bool
if 2+2 == 4:
sane_world = True
else:
sane_world = False
Note that, although this syntax does allow tuple packing, it does *not* allow one
to annotate the types of variables when tuple unpacking is used::
# Tuple packing with variable annotation syntax
t: Any = (1, 2, 3)
# Tuple unpacking with type comments
x, y, z = t # type: int, int, int
# Tuple unpacking with variable annotation syntax
x: int
y: int
z: int
x, y, z = t
Omitting a default value leaves the variable uninitialized::
a: int
print(a) # raises NameError
However, annotating a local variable will cause the interpreter to always make
it a local::
def f():
a: int
print(a) # raises UnboundLocalError
# Commenting out the `a: int` makes it a NameError!
as if the code were::
def f():
if False: a = 0
print(a) # raises UnboundLocalError
Class Variable Typing
*********************
Adding variable types allow for us annotate the types of instance variables 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 looks as follows::
class Starship:
captain: str # instance variable without default
damage: int = 0 # instance variable with default
stats: class Dict[str, int] = {} # class variable with initialization
Duplicate annotations
*********************
Any duplicate type annotations will be ignored::
a: int
a: int # Doesn't do anything.
The Python compiler will not validate the type expression, and leave it to
the type checker to complain. The above code will be allowed by the
compiler at runtime.
Where annotations aren't allowed
********************************
It's illegal to attempt to annotate ``global`` and ``nonlocal``::
def f():
global x: int # SyntaxError
The reason is that ``global`` and ``nonlocal`` don't own variables;
therefore, the type annotations belong in the scope owning the variable.
In addition, you cannot annotate variable used in a ``for`` or ``with``
statement; they must be annotated ahead of time, in a similar manner to tuple
unpacking::
a: int
for a in my_iter:
f: MyFile
with myfunc() as f:
# ...
Capturing Types at Runtime
**************************
In order to capture variable types that are usable at runtime, we store the
types in `__annotations__` as dictionaries at various levels. At each level (for
example, global), the types dictionary would be stored in the `__annotations__`
dictionary for that given level. Here is an example for both global and class
level types::
# print global type annotations
players: Dict[str, Player]
print(__annotations__)
# print class type annotations
class Starship:
hitpoints: class int = 50
stats: class Dict[str, int] = {}
shield: int = 100
captain: str # no initial value
print(Starship.__annotations__)
A note about locals -- the value of having annotations available locally does not
offset the cost of having to create and populate the annotations dictionary on
every function call.
These annotations would be printed out from the previous program as follows::
{'players': Dict[str, Player]}
{'hitpoints': ClassVar[int],
'stats': ClassVar[Dict[str, int]],
'shield': int,
'captain': str
}
Mypy supports allowing `# type` on assignments to instance variables and other things.
In case you prefer annotating instance variables in `__init__` or `__new__`, you can
also annotate variable types for instance variables in methods. Despite this,
`__annotations__` will not be updated for that class.
Backwards Compatibility
=======================
Copyright
=========
This document has been placed in the public domain.