When and how to evaluate Python annotations

submited by
Style Pass
2021-06-09 21:30:04

Welcome to LWN.net The following subscription-only content has been made available to you by an LWN subscriber. Thousands of subscribers depend on LWN for the best news from the Linux and free software communities. If you enjoy this article, please consider subscribing to LWN. Thank you for visiting LWN.net!

The following subscription-only content has been made available to you by an LWN subscriber. Thousands of subscribers depend on LWN for the best news from the Linux and free software communities. If you enjoy this article, please consider subscribing to LWN. Thank you for visiting LWN.net!

Annotations in Python came late to the party; they were introduced in Python 3 as a way to attach information to functions describing their arguments and return values. While that mechanism had obvious applications for adding type information to Python functions, standardized interpretations for the annotations came later with type hints. But evaluating the annotations at function-definition time caused some difficulties, especially with respect to forward references to type names, so a Python Enhancement Proposal (PEP) was created to postpone their evaluation until they were needed. The PEP-described behavior was set to become the default in the upcoming Python 3.10 release, but that is not to be; the postponement of evaluation by default has itself been postponed in the hopes of unwinding things.

It is, as might be guessed, a bit of a tangle, which will require some backstory in order to fully understand things. In 2006, PEP 3107 ("Function Annotations ") was adopted for Python 3 to allow the value of arbitrary expressions to be associated with a function's arguments and its return value. The __annotations__ dictionary associated with the function would then contain the data. An example from the PEP is instructive, even if it is somewhat contrived: For example, the following annotation: def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9): ... would result in an __annotations__ mapping of {'a': 'x', 'b': 11, 'c': list, 'return': 9} The return key was chosen because it cannot conflict with the name of a parameter; any attempt to use return as a parameter name would result in a SyntaxError.

Leave a Comment