Skip to content
Advertisement

Different runtime behavior for non-generic typing

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.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement