[Python-ideas] Trial balloon: adding variable type declarations in support of PEP 484

Guido van Rossum guido at python.org
Fri Aug 5 12:41:13 EDT 2016


On Fri, Aug 5, 2016 at 9:23 AM, Eric Snow <ericsnowcurrently at gmail.com> wrote:
> The only thing I'd like considered further is exposing the annotations
> at runtime.  Others have already suggested this and Guido has
> indicated that they will not be evaluated at runtime.  I think that's
> fine in the short term, but would still like future support considered
> (or at least not ruled out) for runtime evaluation (and availability
> on e.g. func/module/cls.__var_annotations__).

Actually my current leaning is as follows:

- For annotated module globals, the type annotations will be evaluated
and collected in a dict named __annotations__ (itself a global
variable in the module).

- Annotations in class scope are evaluated and collected in a dict
named __annotations__ on the class (itself a class attribute). This
will contain info about annotated class variables as well as instance
variables. I'm thinking that the class variable annotations will be
wrapped in a `ClassVar[...]` object.

- Annotations on local variables are *not* evaluated (because it would
be too much of a speed concern) but the presence of an annotation
still tells Python that it's a local variable, so a "local variable
slot" is reserved for it (even if no initial value is present), and if
you annotate it without initialization you may get an
UnboundLocalError, e.g. here:

def foo():
    x: int
    print(x)  # UnboundLocalError

This is the same as e.g.

def bar():
    if False: x = 0
    print(x)  # UnboundLocalError

> If performance is the main concern, we should be able to add compiler
> support to evaluate/store the annotations selectively on a per-file
> basis, like we do for __future__ imports.  Alternately, for functions
> we could evaluate the annotations in global/non-local scope at the
> time the code object is created (i.e. when handling the MAKE_FUNCTION
> op code), rather than as part of the code object's bytecode.

I considered that, but before allowing that complexity, I think we
should come up with a compelling use case (not a toy example). This
also produces some surprising behavior, e.g. what would the following
do:

def foo():
    T = List[int]
    a: T = []
    # etc.

If we evaluate the annotation `T` in the surrounding scope, it would
be a NameError, but a type checker should have no problem with this
(it's just a local type alias).

> Again, not adding the support right now is fine but we should be
> careful to not preclude later support.  Of course, this assumes
> sufficient benefit from run-time access variable type annotations,
> which I think is weaker than the argument for function annotations.
> We should still keep this possible future aspect in mind though.

The problem with such a promise is that it has no teeth, until the
future behavior is entirely specified, and then we might as well do it
now. My current proposal (no evaluation of annotations for locals)
means that you can write complete nonsense there (as long as it is
*syntactically* correct) and Python would allow it. Surely somebody is
going to come up with a trick to rely on that and then the future
development would break their code.

> Relatedly, it would be nice to address the future use of this syntax
> for more generic variable annotations (a la function annotations), but
> that's less of a concern for me.  The only catch is that making
> "class" an optional part of the syntax impacts the semantics of the
> more generic "variable annotations".  However, I don't see "class" as
> a problem, particularly if it is more strongly associated with the
> name rather than the annotation, as you've suggested below.  If
> anything it's an argument *for* your recommendation. :)

I'm unclear on what you mean by "more generic variable annotations".
Do you have an example?

> One question: Will the use of the "class" syntax only be allowed at
> the top level of class definition blocks?

Oooh, very good question. I think that's the idea. Enforcement can't
happen directly at the syntactic level, but it can be checked in the
same way that we check that e.g. `return` only occurs in a function or
`break` and `continue` only in a loop.

-- 
--Guido van Rossum (python.org/~guido)


More information about the Python-ideas mailing list