Consider these two scenarios:
from queue import Queue my_q: Queue[int] = Queue() print(f"queue={my_q}")
and
from queue import Queue class MyClass(object): def __init__(self): self.my_q: Queue[int] = Queue() my_class = MyClass() print(f"queue={my_class.my_q}")
Running the former (expectedly) throws a TypeError
:
$ python3 run.py Traceback (most recent call last): File "test.py", line 3, in <module> my_q: Queue[int] = Queue() TypeError: 'type' object is not subscriptable
However the latter doesn’t, and proceeds to the print statement without issue:
$ python3 run.py queue=<queue.Queue object at 0x7fb40265f730>
I would expect a TypeError
in both cases. Why is there no TypeError
when the Queue[int]
type is inside of a class’s __init__
method?
Advertisement
Answer
Both module and class annotations are accessible at runtime, and thus evaluated:
# example.py a: int = 4 print(__annotations__) # {'a': <class 'int'>}
In contrast, the annotations local to a function are not accessible and thus never evaluated.
As per PEP 3107, function parameter annotations are evaluated and available at runtime:
Once compiled, a function’s annotations are available via the function’s
__annotations__
attribute.
As per PEP 526, module and class level annotations of simple names are evaluated and available at runtime. Annotations inside functions are explicitly not evaluated or stored for efficiency:
In addition, at the module or class level, if the item being annotated is a simple name, then it and the annotation will be stored in the
__annotations__
attribute of that module or class (mangled if private) as an ordered mapping from names to evaluated annotations.
[…]
Also 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.
With PEP 563 / Python 3.10, no annotations are evaluated immediately anymore. However, annotations of module, class, and function-parameter names may still be evaluated lazily and should thus be valid expressions. Annotations local to functions remain inaccessible and thus unevaluated.
Note that as per PEP 526, local variable annotations are not evaluated at all since they are not accessible outside of the function’s closure.