Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable type annotation NameError inconsistency

In Python 3.6, the new Variable Annotations were introduced in the language.

But, when a type does not exist, the two different things can happen:

>>> def test():
...     a: something = 0
... 
>>> test()
>>> 
>>> a: something = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'something' is not defined

Why is the non-existing type handling behavior different? Would not it potentially cause one to overlook the undefined types in the functions?


Notes

Tried with both Python 3.6 RC1 and RC2 - same behavior.

PyCharm highlights something as "unresolved reference" in both inside and outside the function.

like image 290
alecxe Avatar asked Dec 22 '16 21:12

alecxe


3 Answers

The behaviour of the local variable (i.e. inside the function) is at least documented in the section Runtime Effects of Type Annotations:

Annotating a local variable will cause the interpreter to treat it as a local, even if it was never assigned to. Annotations for local variables will not be evaluated:

def f():
    x: NonexistentName  # No error.

And goes on to explain the difference for global variables:

However, if it is at a module or class level, then the type will be evaluated:

x: NonexistentName  # Error!
class X:
    var: NonexistentName  # Error!

The behaviour seems surprising to me, so I can only offer my guess as to the reasoning: if we put the code in a module, then Python wants to store the annotations.

# typething.py
def test():
    a: something = 0

test()


something = ...

a: something = 0

Then import it:

>>> import typething
>>> typething.__annotations__
{'a': Ellipsis}
>>> typething.test.__annotations__
{}

Why it's necessary to store it on the module object, but not on the function object - I don't have a good answer yet. Perhaps it is for performance reasons, since the annotations are made by static code analysis and those names might change dynamically:

...the value of having annotations available locally does not offset the cost of having to create and populate the annotations dictionary on every function call. Therefore annotations at function level are not evaluated and not stored.

like image 165
wim Avatar answered Oct 16 '22 07:10

wim


The most direct answer for this (to complement @wim's answer) comes from the issue tracker on Github where the proposal was discussed:

[..] Finally, locals. Here I think we should not store the types -- the value of having the annotations available locally is just not enough to offset the cost of creating and populating the dictionary on each function call.

In fact, I don't even think that the type expression should be evaluated during the function execution. So for example:

def side_effect():
    print("Hello world")
def foo():
    a: side_effect()
    a = 12
    return a
foo()

should not print anything. (A type checker would also complain that side_effect() is not a valid type.)

From the BDFL himself :-) nor a dict created nor evaluation being performed.

Currently, function objects only store annotations as supplied in their definition:

def foo(a: int): 
    b: int = 0

get_type_hints(foo)   # from typing
{'a': <class 'int'>}

Creating another dictionary for the local variable annotations was apparently deemed too costly.

like image 7
Dimitris Fasarakis Hilliard Avatar answered Oct 16 '22 07:10

Dimitris Fasarakis Hilliard


You can go to https://www.python.org/ftp/python/3.6.0/ and download the RC2 version to test annotations but the released version as wim said is not yet released. I did however downloaded and tried your code using the Python3.6 interpreter and no errors showed up.

like image 4
Dilmer Avatar answered Oct 16 '22 08:10

Dilmer