アノテーション

【Python】型ヒント(変数・関数の型アノテーション)の基本

【Python】型ヒント(変数・関数の型アノテーション)の基本

Pythonにおける型ヒント(変数・関数の型アノテーション)の基本について解説します。

型ヒント(変数・関数の型アノテーション)

プログラミング言語には、静的型付け言語動的型付け言語といったものがあります。前者の静的型付け言語は、変数宣言の際に型の指定が必要でコンパイル時などにチェックが行われます。一方で後者の動的型付け言語は、変数に型の指定は不要で実行時に変数の型が定まります。

Pythonは、動的型付け言語です。そのため、変数や関数の宣言時には特に型の指定をすることなくプログラミング可能です。C/C++やJava等を先に勉強をしてPythonに入ると型を指定しないことに違和感を持つ人もいるかもしれません。

Pythonではバージョン3.5から型ヒントがサポートされており、変数や関数に型アノテーションとして型情報を指定できるようになっています。

本記事では、Pythonの型ヒント(変数・関数の型アノテーション)について基本的な使い方を紹介します。

型ヒント(型アノテーション)の使い方

変数の型アノテーション

変数に型アノテーションをつける場合には、以下のように変数の後ろに「: 型」というような形で指定します。

x: int = 10
y: float = 0.25
s: str = "テスト"

print(x)
print(y)
print(s)
【実行結果】
10
0.25
テスト

上記例では、xはint型、yはfloat型、sはstr型であることを示しています。

リスト・タプル・辞書の型アノテーション

リスト・タプル・辞書の型アノテーションの方法としては、Pythonバージョンによって少し使い方が異なっています。

typingモジュールから型をインポート

Pythonの型ヒントに関するモジュールはtypingモジュールです。以下のようにtypingモジュールからList, Tuple, Dictという型をインポートすることで、リスト・タプル・辞書の型アノテーションが可能です。

from typing import Dict, List, Tuple

value_list: List[int] = [1, 2, 3, 4, 5]
value_tuple: Tuple[int, str] = (1, "abc")
value_dict: Dict[int, str] = {1: "a", 2: "b", 3: "c"}

print(value_list)
print(value_tuple)
print(value_dict)
【実行結果】
[1, 2, 3, 4, 5]
(1, 'abc')
{1: 'a', 2: 'b', 3: 'c'}

リストでは、List[型]のようにリストに含まれる型を指定します。Pythonのリストでは複数の型を含めることができますが、型アノテーションでは複数の型を指定することができません。

タプルの場合は、リストとは少し違いタプルが含む要素の型をTuple[型, 型, …]のように列挙して指定します。

辞書の場合は、Dict[キーの型, 値の型]という形で指定します。

Python3.9以降 (List, Tuple, Dictは非推奨)

Python3.9以降では、typingモジュールからListやTuple、Dictをインポートしなくても以下のように記載できるようになっています。なお、List, Tuple, Dictは3.9からは非推奨(deprecated)になっています。

# Python 3.9以降
# List, Tuple, Dictは非推奨
value_list: list[int] = [1, 2, 3, 4, 5]
value_tuple: tuple[int, str] = (1, "abc")
value_dict: dict[int, str] = {1: "a", 2: "b", 3: "c"}

print(value_list)
print(value_tuple)
print(value_dict)
【実行結果】
[1, 2, 3, 4, 5]
(1, 'abc')
{1: 'a', 2: 'b', 3: 'c'}

typingモジュールからインポートする必要がなくなり、シンプルに記述できるようになっています。

型エイリアス

ある型に対して別の型名を使用することを型エイリアスと言います。エイリアスとは「別名」という意味です。型エイリアスを使う場合には以下のように使用します。

# 型エイリアスの作成
Vector = list[float]

new_vector: Vector = [1.0, 2, 0, 3.0]
print(new_vector)
【実行結果】
[1.0, 2, 0, 3.0]

上記例では、list[float]というfloat型のリストの別名としてVectorとして定義しています。

このように型エイリアスを使うと、型アノテーションの表記をシンプルにすることができますし、型が持つ意味(上記例だとfloatの値を持つベクトル)を明確に分かるようにすることが可能です。

新しいカスタム型の作成

異なる新しいカスタム型を作成するには、以下のようにtypingモジュールのNewTypeを用いて新しい型を定義します。

from typing import NewType

# 新しいUserId型を作る
UserId = NewType("UserId", int)

# UserId型の変数を用意
test_id = UserId(12345)
print(test_id)
【実行結果】
12345

上記例では、UserIdという新しい型を定義しています。とはいっても実体はint型です。UserId型では、intの全ての演算を行うことができます。

関数の型アノテーション

関数に型アノテーションをつける場合には、以下のような形で指定します。

  • 引数:変数の後ろに「: 型」というような形で指定
  • 返り値:「-> 型」というような形で指定

具体的な使い方の例を見てみましょう。

# 型アノテーションを指定しない場合
def func(x, y):
    return x * y


# 型アノテーションを指定する場合
def func_annotation(x: int, y: int) -> int:
    return x * y


if __name__ == "__main__":
    val1 = 10
    val2 = 20
    print(func(val1, val2))
    print(func_annotation(val1, val2))
【実行結果】
200
200

関数funcは関数アノテーションを使用していない通常の定義方法で、関数func_annotationは、関数アノテーションを使用している例です。実行する内容、結果はどちらも同じです。

上記func_annotationの例では、int型の引数xとyを受け取って返却値としてint型を返すことを明示しています。型の部分には、floatやstr等を使用することができます。

__annotations__属性

関数アノテーションを使用する場合は、以下のように__annotations__属性で型アノテーションの情報を参照することができます。

# 型アノテーションを指定する場合
def func_annotation(x: int, y: int) -> int:
    return x * y


if __name__ == "__main__":
    print(func_annotation.__annotations__)
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

上記のように引数や返却値の型を確認できます。

Pythonの型ヒントはただの注釈でしかない

これまで型ヒントについて紹介してきましたが、型ヒントはただの注釈でしかないことを覚えておく必要があります。型ヒントを指定してもプログラム実行に制約がかかるわけではありませんし、Pythonは変数や関数の型アノテーションを強制もしません。

例えば、以下のようにした場合でも普通に実行できます。

def func_annotation(x: int, y: int) -> int:
    return x * y


if __name__ == "__main__":
    # 型アノテーションに従った使用方法
    val1 = 10
    val2 = 20
    print(func_annotation(val1, val2))

    # Pythonの型ヒントはただの注釈でしかないため強制力はない
    val1 = "test"
    val2 = 3
    print(func_annotation(val1, val2))
【実行結果】
200
testtesttest

上記例での二つ目の使い方では、xに該当する位置に”test”というstrの変数を指定しており、型アノテーションには反しています。この場合でも、func_annotation内で定義している「x * y」は評価できるため、結果は「testtesttest」と文字列を3回繰り返す結果となっています。

例えば、関数型プログラミング言語のHaskellには強力な型システムがあり、本記事で紹介したような型アノテーションや型エイリアス、カスタム型の定義に相当することができます。Haskellでは、厳密に型の定義に従う必要があるため、上記例のようなアノテーションに従わない関数の使い方は許容されません。

なお、Pythonで型チェックをするような場合には、mypyというモジュールを使用することができます。mypyについては「mypyを使用した型チェックの方法」にまとめていますので興味があれば参考にしてください。

型ヒントはPythonにおいては必須のものではありませんが、利用者に変数や関数の使い方を明示する意味で使用を検討するのもよいでしょう。プロジェクト単位で使用する/しないを明確に決めて使うようにするとよいと思います。

まとめ

Pythonにおける型ヒント(変数・関数の型アノテーション)の基本について解説しました。

Pythonではバージョン3.5から型ヒントがサポートされており、変数や関数に型アノテーションとして型の情報を指定できるようになっています。本記事では、変数や関数での基本的な指定方法について紹介しています。

注意点として型ヒントはただの注釈でしかないことを覚えておく必要があります。型ヒントを指定してもプログラム実行に制約がかかるわけではありませんし、Pythonは変数や関数の型アノテーションを強制もしません。

型ヒントはPythonにおいては必須のものではありませんが、利用者に変数や関数の使い方を明示する意味で使用を検討するのもよいでしょう。プロジェクト単位で使用する/しないを明確に決めて使うようにするとよいかなと思います。

Note

型ヒントに関する公式ドキュメントはこちらを参照してください。