What are variable annotations?
Asked Answered
N

2

133

Python 3.6 is about to be released. PEP 494 -- Python 3.6 Release Schedule mentions the end of December, so I went through What's New in Python 3.6 to see they mention the variable annotations:

PEP 484 introduced standard for type annotations of function parameters, a.k.a. type hints. This PEP adds syntax to Python for annotating the types of variables including class variables and instance variables:

primes: List[int] = []

captain: str  # Note: no initial value!

class Starship:
     stats: Dict[str, int] = {}

Just as for function annotations, the Python interpreter does not attach any particular meaning to variable annotations and only stores them in a special attribute __annotations__ of a class or module. In contrast to variable declarations in statically typed languages, the goal of annotation syntax is to provide an easy way to specify structured type metadata for third party tools and libraries via the abstract syntax tree and the __annotations__ attribute.

So from what I read they are part of the type hints coming from Python 3.5, described in What are Type hints in Python 3.5.

I follow the captain: str and class Starship example, but not sure about the last one: How does primes: List[int] = [] explain? Is it defining an empty list that will just allow integers?

Notarize answered 11/10, 2016 at 7:0 Comment(3)
Type hints do not do any type checking. primes: List[int] = [] is just an empty list as primes = []. The difference is that you are claiming that primes is meant to contain only ints and 3rd party applications might type check your program to verify this claim, but when you run the code in any python interpreter that's just the same as writing primes = [], and thus doing primes: List[int] = []; primes.append("string") is still valid.Precede
@Precede yes, good point. As Jim Fasarakis-Hilliard describes in his answer to What are Type hints in Python 3.5, why type hintsHelps type checkers, helps with documentation and Helps IDEs develop more accurate and robust tools. Taken from PEP 526 -- Syntax for Variable Annotations, Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.Notarize
Does this answer your question? What are type hints in Python 3.5?Phip
E
58

Everything between : and the = is a type hint, so primes is indeed defined as List[int], and initially set to an empty list (and stats is an empty dictionary initially, defined as Dict[str, int]).

List[int] and Dict[str, int] are not part of the next syntax however, these were already defined in the Python 3.5 typing hints PEP. The 3.6 PEP 526 – Syntax for Variable Annotations proposal only defines the syntax to attach the same hints to variables; before you could only attach type hints to variables with comments (e.g. primes = [] # List[int]).

Both List and Dict are Generic types, indicating that you have a list or dictionary mapping with specific (concrete) contents.

For List, there is only one 'argument' (the elements in the [...] syntax), the type of every element in the list. For Dict, the first argument is the key type, and the second the value type. So all values in the primes list are integers, and all key-value pairs in the stats dictionary are (str, int) pairs, mapping strings to integers.

See the typing.List and typing.Dict definitions, the section on Generics, as well as PEP 483 – The Theory of Type Hints.

Like type hints on functions, their use is optional and are also considered annotations (provided there is an object to attach these to, so globals in modules and attributes on classes, but not locals in functions) which you could introspect via the __annotations__ attribute. You can attach arbitrary info to these annotations, you are not strictly limited to type hint information.

You may want to read the full proposal; it contains some additional functionality above and beyond the new syntax; it specifies when such annotations are evaluated, how to introspect them and how to declare something as a class attribute vs. instance attribute, for example.

Expository answered 11/10, 2016 at 7:8 Comment(2)
May I consider type hints as a kind of "machine-readable" comments, since they do not affect how the code runs (except for an obj.__annotations__ attribute)?Huntress
@iBug: annotations are machine-readable comments, insofar that comments are human-readable annotations anyway. :-)Expository
I
92

What are variable annotations?

Variable annotations are just the next step from # type comments, as they were defined in PEP 484; the rationale behind this change is highlighted in the respective section of PEP 526.

So, instead of hinting the type with:

primes = []  # type: List[int]

New syntax was introduced to allow for directly annotating the type with an assignment of the form:

primes: List[int] = []

which, as @Martijn pointed out, denotes a list of integers by using types available in typing and initializing it to an empty list.

What changes does it bring?

The first change introduced was new syntax that allows you to annotate a name with a type, either standalone after the : character or optionally annotate while also assigning a value to it:

annotated_assignment_stmt ::=  augtarget ":" expression ["=" expression]

So the example in question:

   primes: List[int] = [ ]
#    ^        ^         ^
#  augtarget  |         |
#         expression    |
#                  expression (optionally initialize to empty list)

Additional changes were also introduced along with the new syntax; modules and classes now have an __annotations__ attribute (as functions have had since PEP 3107 -- Function Annotations) in which the type metadata is attached:

from typing import get_type_hints  # grabs __annotations__

Now __main__.__annotations__ holds the declared types:

>>> from typing import List, get_type_hints
>>> primes: List[int] = []
>>> captain: str
>>> import __main__
>>> get_type_hints(__main__)
{'primes': typing.List<~T>[int]}

captain won't currently show up through get_type_hints because get_type_hints only returns types that can also be accessed on a module; i.e., it needs a value first:

>>> captain = "Picard"
>>> get_type_hints(__main__)
{'primes': typing.List<~T>[int], 'captain': <class 'str'>}

Using print(__annotations__) will show 'captain': <class 'str'> but you really shouldn't be accessing __annotations__ directly.

Similarly, for classes:

>>> get_type_hints(Starship)
ChainMap({'stats': typing.Dict<~KT, ~VT>[str, int]}, {})

Where a ChainMap is used to grab the annotations for a given class (located in the first mapping) and all annotations defined in the base classes found in its mro (consequent mappings, {} for object).

Along with the new syntax, a new ClassVar type has been added to denote class variables. Yup, stats in your example is actually an instance variable, not a ClassVar.

Will I be forced to use it?

As with type hints from PEP 484, these are completely optional and are of main use for type checking tools (and whatever else you can build based on this information). It is to be provisional when the stable version of Python 3.6 is released so small tweaks might be added in the future.

Imogeneimojean answered 11/10, 2016 at 8:21 Comment(1)
Things have changed a bit in Python 3.9+ with the introduction of PEP 585. This changed your example to primes: list[int] = [] or even simpler primes = list[int]().Naca
E
58

Everything between : and the = is a type hint, so primes is indeed defined as List[int], and initially set to an empty list (and stats is an empty dictionary initially, defined as Dict[str, int]).

List[int] and Dict[str, int] are not part of the next syntax however, these were already defined in the Python 3.5 typing hints PEP. The 3.6 PEP 526 – Syntax for Variable Annotations proposal only defines the syntax to attach the same hints to variables; before you could only attach type hints to variables with comments (e.g. primes = [] # List[int]).

Both List and Dict are Generic types, indicating that you have a list or dictionary mapping with specific (concrete) contents.

For List, there is only one 'argument' (the elements in the [...] syntax), the type of every element in the list. For Dict, the first argument is the key type, and the second the value type. So all values in the primes list are integers, and all key-value pairs in the stats dictionary are (str, int) pairs, mapping strings to integers.

See the typing.List and typing.Dict definitions, the section on Generics, as well as PEP 483 – The Theory of Type Hints.

Like type hints on functions, their use is optional and are also considered annotations (provided there is an object to attach these to, so globals in modules and attributes on classes, but not locals in functions) which you could introspect via the __annotations__ attribute. You can attach arbitrary info to these annotations, you are not strictly limited to type hint information.

You may want to read the full proposal; it contains some additional functionality above and beyond the new syntax; it specifies when such annotations are evaluated, how to introspect them and how to declare something as a class attribute vs. instance attribute, for example.

Expository answered 11/10, 2016 at 7:8 Comment(2)
May I consider type hints as a kind of "machine-readable" comments, since they do not affect how the code runs (except for an obj.__annotations__ attribute)?Huntress
@iBug: annotations are machine-readable comments, insofar that comments are human-readable annotations anyway. :-)Expository

© 2022 - 2024 — McMap. All rights reserved.