コンテンツにスキップ

Python/型ヒント

出典: フリー教科書『ウィキブックス(Wikibooks)』

型ヒント(Type Hints)は、Python 3.5以降で導入された機能で、変数や関数の引数、返り値などに対して、その期待される型情報を注釈として記述することができます。型ヒントは静的型付けの一形態であり、コードの可読性を高め、静的解析ツールによるチェックを容易にするのに役立ちます。

以下は型ヒントの基本的な使い方です:

# 型ヒントを用いた関数定義の例
def greet(name: str) -> str:
    return f"Hello, {name}"

上記の例では、greet関数の引数nameの型をstr(文字列)として指定し、返り値の型もstrとして指定しています。

型ヒントの一般的な使用法

[編集]
  1. 基本型の指定: Pythonの組み込み型(例:int, float, str, bool)をそのまま使用します。
  2. コンテナ型の指定: 型情報を伴ったコンテナ型を表すことができます。例:
    • List[int]: 整数型のリスト
    • Dict[str, int]: 文字列をキーとし、整数を値とする辞書
    • Tuple[str, int, bool]: 異なる型を持つタプル
  3. ユーザー定義型: クラスやタイプエイリアスを用いることで、自作の型を型ヒントに利用できます。
  4. ユニオン型: 複数の型を許容する場合、Unionを用います。例:Union[int, float]は整数または浮動小数点数を表します。

型ヒントは実行時の挙動には影響を与えませんが、IDEや静的解析ツール(例:mypy)を利用することで、型に基づいたエラーチェックや補完機能を活用できます。

型アノテーション

[編集]

型アノテーション(Type Annotation)は、型ヒントの具体的な記述形式を指します。変数や関数、クラス属性に型情報を追加することで、可読性と保守性が向上します。

変数への型アノテーション

[編集]
x: int = 5

ここで、: intは変数xが整数型であることを示しています。

関数への型アノテーション

[編集]
def add(a: int, b: int) -> int:
    return a + b

引数abint型、返り値もint型であることを明示しています。

クラス属性への型アノテーション

[編集]
class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name: str = name
        self.age: int = age

クラスPersonname属性とage属性がそれぞれstr型とint型であることを示しています。

__annotations__属性

[編集]

Pythonのすべての関数、クラス、モジュールには__annotations__という特別な属性が存在します。この属性は型アノテーションの情報を辞書形式で保持しています。

以下はその例です:

x: int = 10
y: str = "hello"

def greet(name: str) -> str:
    return f"Hello, {name}"

print(__annotations__)  # {'x': <class 'int'>, 'y': <class 'str'>}
print(greet.__annotations__)  # {'name': <class 'str'>, 'return': <class 'str'>}

このように、__annotations__属性を用いることで、プログラムの型情報を動的に取得することが可能です。

型アノテーションの効果と限界

[編集]

型アノテーションは実行時には影響を与えず、型情報は静的解析やコード補完に利用されるのみです。そのため、型安全性は完全には保証されません。ただし、以下のような利点があります:

  1. バグの早期発見: IDEや静的解析ツールが型の不一致を検出可能。
  2. コードの可読性向上: 型情報が明示されることで、コードの意図が伝わりやすくなる。
  3. 保守性の向上: 複数人での開発や大規模プロジェクトで特に有用。

一方で、型アノテーションが冗長になる場合もあり、実際の用途に応じて適切なバランスを取ることが重要です。

型アノテーションを使わない場合の問題例

[編集]

以下は型アノテーションを使用しない場合の例です:

def total(*args):
    if len(args) == 0:
        return 0
    it = iter(args)
    result = next(it)
    for i in it:
        result += i
    return result

print(f"""
{total()=}
{total(1, 2, 3)=}
{total(*(i for i in range(10)))=}
{total(*(1.0 * i for i in range(10)))=}
{total(False, True)=}
{total("abc", "def", "ghi")=}
{total([0, 1, 2], [3, 4, 5], [6, 7, 8])=}
""")

実行結果:

total()=0
total(1, 2, 3)=6
total(*(i for i in range(10)))=45
total(*(1.0 * i for i in range(10)))=45.0
total(False, True)=1
total("abc", "def", "ghi")='abcdefghi'
total([0, 1, 2], [3, 4, 5], [6, 7, 8])=[0, 1, 2, 3, 4, 5, 6, 7, 8]

このコードでは、異なる型の値(整数、浮動小数点数、文字列、リストなど)が渡されることで意図しない動作を招く可能性があります。

型アノテーションを用いた改善例

[編集]

以下は型アノテーションを用いて修正した例です:

from typing import Union

Number = Union[int, float]

def total(*args: Number) -> Number:
    if len(args) == 0:
        return 0
    it = iter(args)
    result = next(it)
    for i in it:
        result += i
    return result

print(f"""
{total()=}
{total(1, 2, 3)=}
{total(*(i for i in range(10)))=}
{total(*(1.0 * i for i in range(10)))=}
""")

MyPyの静的検査結果: 型アノテーションがあることで、str型やlist型の不正な引数が渡された場合にエラーが検出されます。例:

main.py:13: error: Argument 1 to "total" has incompatible type "str"; expected "Union[int, float]"
main.py:13: error: Argument 1 to "total" has incompatible type "List[int]"; expected "Union[int, float]"

参考文献

[編集]