[Python-ideas] Delay evaluation of annotations

Steven D'Aprano steve at pearwood.info
Thu Sep 22 23:06:16 EDT 2016


On Thu, Sep 22, 2016 at 07:21:18PM +0000, אלעזר wrote:
> On Thu, Sep 22, 2016 at 9:43 PM Steven D'Aprano <steve at pearwood.info> wrote:
> 
> > On Thu, Sep 22, 2016 at 05:19:12PM +0000, אלעזר wrote:
> > > Hi all,
> > >
> > > Annotations of function parameters and variables are evaluated when
> > > encountered.
> >
> > Right, like all other Python expressions in general, and specifically
> > like function parameter default arguments.
> >
> 
> Just because you call it "expression", when for most purposes it isn't - it
> is an annotation.

It is *both*. It's an expression, because it's not a statement or a 
block. You cannot write:

def func(arg1: while flag: sleep(1), arg2: raise ValueError): 
    ...

because the annotation must be a legal Python expression, not a code 
block or a statement. It's an annotation because that's the 
specific *purpose* of the expression in that context.

As an analogy: would you argue that it is wrong to call the for-loop 
iterable an expression?

    for <target-list> in <expression>:
        block

I trust that you understand that the loop iterable can be any expression 
that evaluates to an iterable. Well, annotations can be any expression 
that evaluates to anything at all, but for the purposes of type 
checking, are expected to evaluate to a string or a type object.

In the case of function annotations, remember that they can be any 
legal Python expression. They're not even guaranteed to be type 
annotations. Guido has expressed a strong preference that they are only 
used as type annotations, but he hasn't yet banned other uses (and I 
hope he doesn't), so any "solution" for a type annotation problem must 
not break other uses.


> "Expression" is something that you need its value right
> now, and "annotation" is something that, well, annotates the code you see
> right now.

Right. In the case of Python, function annotations **do** have a runtime 
effect: the expressions are evaluated, and the evaluated results are 
assigned in function.__annotations__ and made available for runtime 
introspection.

Don't think that function annotations are **only** for the static type 
checker. Python is a much richer language than that! 


> > > It is
> > > also easy to forget, and the result might be a (very uninteresting)
> > > exception in certain untested paths, e.g. inside functions.
> >
> > Unlikely, unless you're talking about functions nested inside other
> > functions, or unusual (but legal and sometimes useful) conditional
> > definitions:
>
> I was thinking about the former, but yeah, uncovered code will fail at
> runtime, possibly in production, for *no* real reason. I do not claim that
> this is common, but it is definitely unnecessary - unlike initialization
> expressions.

Unnecessary?

class MyClass:
    pass


def function(arg: MyCalss):
    ...

I want to see an immediate NameError here, thank you very much, even if 
I'm not running a static checker. I don't want to have to manually call:

function.__annotations__['arg']()

to see whether or not the annotation is valid.

I accept that using strings as forward annotations is not a foolproof 
solution either:


def function(arg: 'MyCalss'):
    ...


but let's not jump into a "fix" that actually makes things worse.

 
> > if condition:
> >     # forward reference to MyClass
> >     def f(arg:'MyClass'): ...
> > else:
> >     # oops, untested path
> >     def f(arg:MyClass): ...
> >
> > class MyClass: ...
> >
> >
> > But generally speaking, that sort of code is unusual, and besides, if
> > you're doing this, either the static type checker won't be able to cope
> > with it at all (in which case there's little point in annotating the
> > function), or it will cope, and detect the invalid annotation.
> >
> Why would it detect invalid annotation here? It shouldn't.

MyClass doesn't exist at that point, so it is in invalid annotation.


> > > help editors in syntax highlighting and name lookup.
> >
> > But will harm runtime introspection.
> >
> 
> Very little. And to quote Frank Miller, “An old man dies, a little girl
> lives. Fair trade.”

Not to the old man, and especially not if the little girl is a 
psychopath who grows up to become a mass murdering totalitarian 
dictator.



-- 
Steve


More information about the Python-ideas mailing list