I was recently surprised to see that some of our code had a linter warning on the List
in something equivalent to:
class MyClass:
def method(self):
myVar: List[str] = []
The linter warning is because this file is missing from typing import List
in the imports. So, PyCharm flags this as a linter error. Certainly the code is incorrect. What's surprising is this code runs, and has run properly for months!
Digging in, I found that this:
class MyClass:
def method(self):
myVar: List[str] = []
m = MyClass()
m.method()
And this:
def method(self):
l: List[str] = []
method(None)
run properly, but this:
l: List[str] = []
Raises the expected error!
Traceback (most recent call last):
File ".../scratches/scratch_3.py", line 5, in <module>
l: List[str] = []
NameError: name 'List' is not defined
Why?
I am running:
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
But the code works on several different versions of Python on Windows and Linux (Ubuntu) builds.
Python treats annotations for global & local variables differently, as outlined in PEP 526:
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.
However, if it is at a module or class level, then the type will be evaluated:
x: NonexistentName # Error! class X: var: NonexistentName # Error!
This makes some sense, as module- and class-level annotations can be accessed through the __annotations__
dictionary, so it must evaluate the annotation and record it. On the other hand, local variable annotations can not be programmatically accessed, so there's no point in evaluating them.