[Python-ideas] Delay evaluation of annotations

אלעזר elazarg at gmail.com
Fri Sep 23 06:17:15 EDT 2016


On Fri, Sep 23, 2016 at 6:06 AM Steven D'Aprano <steve at pearwood.info> wrote:

> 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.


Did you just use a false-trichotomy argument? :)


> 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.


This is the situation I'm asking to change


> It's an annotation because that's the
> specific *purpose* of the expression in that context.
>
>
Exactly! Ergo, this is an annotation.


> 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.
>
>
for-loop iterable is an expression, evaluated at runtime, _for_ the
resulting value to be used in computation. A perfectly standard expression.
Nothing fancy.


> 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.
>
>
Must *allow* other use cases. My proposal allows: just evaluate them at the
time of their use, instead at definition time.


>
> > "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!
>
>
function.__annotations__  can have the delayed value, be it a lambda, ast
or string. It can also be computed at the time of access as suggested
earlier.


>
> > > > 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
>

Two things to note here:
A. IDEs will point at this NameError
B. Type checkers catch this NameError
C. Even the compiler can be made to catch this name error, since the name
MyCalss is bound to builtins where it does not exist - you see, name lookup
does happen at compile time anyway. I'm not really suggesting the compiler
should make it error though.
D. Really, where's the error here? if no tool looks at this signature,
there's nothing wrong with it - As a human I understand perfectly. If a
tool will look at it, it will warn or fail, exactly as I would liked it too.

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.
>
>
>
That's not a "fix". I suggest always using the last form - which is already
in common use - with a nicer syntax and semantics, since there's nothing
wrong about it. It is there for a very natural reason.


> > > 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.
>
>
> MyClass does not exist. The _name_ MyClass does, definitely. So it is an
invalid _expression_ but a perfectly valid _annotation_ (as a concept).


> > > > 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.
>
>
:)

Elazar
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160923/1a166ae4/attachment-0001.html>


More information about the Python-ideas mailing list