mypyの型チェックは通るのに実行時に TypeError: 'method' object is not subscriptable が発生する場合

概要

ライブラリのクラスなどの型パラメータを指定するとmypyによる型チェックは通るものの実行時にエラーになってしまう場合があります。 その対処法と、このエラーが発生する理由について書きます。

from multiprocessing import Queue

q: Queue[int] = Queue()
$ mypy main.py
Success: no issues found in 1 source file

$ python main.py 
Traceback (most recent call last):
  File "xxx/main.py", line 3, in <module>
    q: Queue[int] = Queue()
TypeError: 'method' object is not subscriptable

対処法

Using classes that are generic in stubs but not at runtimeに書かれている通り、3種類の対応方法があります。

Python 3.7以上を使っている場合

from __future__ import annotations により実行時エラーが発生しなくなります。

from __future__ import annotations
from multiprocessing import Queue

q: Queue[int] = Queue()

文字列リテラルを使用

from multiprocessing import Queue

q: 'Queue[int]' = Queue()

型チェックとランタイムで切り替え

from multiprocessing import Queue
from typing import TYPE_CHECKING

if TYPE_CHECKING:
  IntQueue = Queue[int]
else:
  IntQueue = Queue

q: IntQueue = Queue()

実行時エラーが発生する理由

PEP 484 には、型ヒントについて次のように書かれています。

Annotations must be valid expressions that evaluate without raising exceptions at the time the function is defined

アノテーションは例外を発生させない式でなければなりません。

Queue[int] を式として評価したとき、 Queue (のメタクラス)が __getitem__ を実装していないためにエラーが発生します。 そのため、 TypeError: 'method' object is not subscriptable というエラーが発生します。

参考