Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

Pythonのtyping.Tupleで可変長のタプルとしてアノテーションする

mypy使っていますか?私は最近プロジェクトにも入れて使っています。

ですが typing.Tuple の扱いがうまく分かっていなくて、こんなプログラムを書いていました。

# 悪い例
import typing


my_tuple: typing.Tuple[str] = ()

my_tuple = ('mymodule.myfunc', 'mymodule.myfunc2')

このプログラムを mypy で型チェックするとこんなエラーがでます。

$ mypy ./tuple.py
tuple.py:4: error: Incompatible types in assignment (expression has type "Tuple[]", variable has type "Tuple[str]")
tuple.py:6: error: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[str]")

Tupleアノテーションは固定長で使うように想定されているようです。

例えば Tuple[str, int, int, int] のようにして、 ('名前', 1991, 11, 5) のような値を入れる使い方でしょうか。 ちょうど、 NamedTuple に近い印象で、1つの「何か」を1つのタプルで表すような使い方です。

可変長のタプルでアノテーションする

可変長のタプルをアノテーションするには Tuple[str, ...] のようにEllipsis ... を使って明示します

# 良い例
import typing


my_tuple: typing.Tuple[str, ...] = ()

my_tuple = ('mymodule.myfunc', 'mymodule.myfunc2')

typing — Support for type hints — Python 3.7.1 documentation

これでうまく動作します。

例えば以下のようにテンプレートになる基底クラスで、期待する設定値のアノテーションをしておくこともできます。

class MyBase:
    SOME_FIELDS: typing.ClassVar[typing.Tuple[str, ...]] = ()

typing.List はもともと可変長の値でチェックできますが、タプルのときは typing.Tuple[str, ...] のようにEllipsisを書くと可変長と解釈されます。