0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonのtypingについて:型ヒントを理解しよう

Posted at

Pythonのtypingモジュールは、コードの可読性を高め、エラーを早期に発見するために「型ヒント(Type Hinting)」を記述する機能を提供します。Pythonは動的型付け言語なので、通常は変数の型を明示的に宣言する必要はありませんが、型ヒントを使うことで、どの変数にどんな型の値が来るかをプログラマが明示できるようになります。

0. 初学者でもわかる簡単な説明

Pythonは柔軟な言語で、例えば変数xに最初は数字を入れて、後から文字列を入れることもできます。


x = 10
x = "こんにちは"

これは便利ですが、大きなプログラムになると「このxって、今、数字だっけ?それとも文字列だっけ?」と混乱することがあります。また、間違った型の値を関数に渡してしまい、実行時にエラーになることもあります。

typingモジュールを使うと、あらかじめ「このxには数字が入る予定だよ」とか「この関数は文字列を受け取って、数字を返すよ」という「ヒント」をコードに書き込むことができます。

これはコードの実行には影響しませんが、プログラマがコードを読みやすくしたり、VS Codeのような開発ツールが「あれ?ここ、数字じゃなくて文字列を渡してるよ!」と教えてくれたりするようになります。車のナビゲーションシステムに「ここを右に曲がって、この道を行くと目的地だよ」と教えてもらうようなものですね。

1. 基本的なこと

型ヒントは、変数、関数の引数、関数の戻り値に対して記述できます。

変数への型ヒント
変数を宣言するときに、コロン(:)と型名を使って型ヒントを付けます。


age: int = 30  # ageは整数型であることを示す
name: str = "Alice" # nameは文字列型であることを示す

関数引数と戻り値への型ヒント
関数の引数には変数と同様にコロンと型名を、戻り値には引数リストの後に->と型名を付けて型ヒントを付けます。


def greet(name: str) -> str:
    # nameは文字列型、戻り値も文字列型であることを示す
    return f"Hello, {name}!"

def add(a: int, b: int) -> int:
    # aとbは整数型、戻り値も整数型であることを示す
    return a + b

主な組み込み型
よく使う型には以下のようなものがあります。

int: 整数

str: 文字列

float: 浮動小数点数

bool: 真偽値(True/False)

list: リスト(例: list[int]で整数のリスト)

dict: 辞書(例: dict[str, int]でキーが文字列、値が整数の辞書)

tuple: タプル(例: tuple[str, int]で文字列と整数のタプル)

set: セット(例: set[str]で文字列のセット)

2. よく使う型ヒント

typingモジュールからインポートして使う、より複雑な型ヒントがあります。

List, Dict, Tuple, Set: これらはPython 3.9以前で使用されていたコレクションの型ヒントです。Python 3.9以降では、組み込みのlist, dictなどを直接list[int]のように使えますが、古いバージョンとの互換性のためにまだ使われることがあります。


from typing import List, Dict

def process_numbers(numbers: List[int]) -> Dict[str, int]:
    # ...

Optional: その値が指定された型、またはNoneの可能性があることを示します。


from typing import Optional

def get_user_name(user_id: int) -> Optional[str]:
    # ユーザーが見つからない場合はNoneを返す可能性
    if user_id == 1:
        return "Alice"
    return None

Union: 複数の型のいずれかであることを示します。


from typing import Union

def process_data(data: Union[str, int]) -> str:
    # dataは文字列型か整数型
    return str(data)

Any: どんな型でも良いことを示します。型チェックを無効にしたい場合などに使いますが、多用すると型ヒントの意味が薄れます。


from typing import Any

def process_anything(item: Any) -> Any:
    return item

Callable: 関数(呼び出し可能オブジェクト)の型ヒントです。引数の型と戻り値の型を指定します。


from typing import Callable

def apply_func(func: Callable[[int, int], int], a: int, b: int) -> int:
    # funcはintを2つ引数にとり、intを返す関数
    return func(a, b)

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

result = apply_func(multiply, 5, 3) # resultは15

TypeVar: ジェネリックな型を定義するために使います。例えば、リストの要素の型に関わらず同じ処理をする関数を定義する際に便利です。


from typing import TypeVar, List

T = TypeVar('T') # 任意の型を表すTを定義

def get_first_element(items: List[T]) -> T:
    return items[0]

numbers = [1, 2, 3]
first_num: int = get_first_element(numbers)

words = ["hello", "world"]
first_word: str = get_first_element(words)

3. サンプルコード

以下に、いくつかのtypingを使ったサンプルコードを示します。


from typing import List, Dict, Optional, Union, Callable, Any

# 1. 基本的な型ヒント
def describe_person(name: str, age: int, is_student: bool) -> str:
    """
    名前、年齢、学生であるかどうかに基づいて人物を説明する関数
    """
    status = "学生" if is_student else "社会人"
    return f"{name} ({age}歳) は{status}です。"

print(describe_person("田中", 25, False))
# print(describe_person(123, "佐藤", True)) # 型チェッカーで警告が出る

# 2. リストと辞書への型ヒント
def calculate_total_price(items: List[Dict[str, Union[str, int]]]) -> int:
    """
    商品リストの合計金額を計算する関数
    itemsは、各要素が'name'(str)と'price'(int)を持つ辞書のリスト
    """
    total = 0
    for item in items:
        # 型チェッカーはitem['price']がintまたはstrであることを認識
        if isinstance(item['price'], int):
            total += item['price']
        else:
            print(f"警告: {item['name']}の価格が整数ではありません。")
    return total

products = [
    {"name": "りんご", "price": 100},
    {"name": "みかん", "price": 80},
    {"name": "バナナ", "price": "エラー"}, # わざと型に合わない値
]

print(f"合計金額: {calculate_total_price(products)}")

# 3. OptionalとUnion
def get_user_email(user_id: int) -> Optional[str]:
    """
    ユーザーIDに基づいてメールアドレスを返す関数。
    ユーザーが見つからない場合はNoneを返す。
    """
    user_data = {
        1: "alice@example.com",
        2: "bob@example.com"
    }
    return user_data.get(user_id) # getメソッドはキーがない場合Noneを返す

email1 = get_user_email(1)
email3 = get_user_email(3)

print(f"ユーザー1のメール: {email1}")
print(f"ユーザー3のメール: {email3}")

def process_input(value: Union[str, int, float]) -> str:
    """
    文字列、整数、浮動小数点数のいずれかの値を受け取り、文字列に変換する。
    """
    return str(value)

print(process_input("Hello"))
print(process_input(123))
print(process_input(45.67))

# 4. Callable
def apply_operation(operation: Callable[[int, int], int], x: int, y: int) -> int:
    """
    2つの整数を受け取り、指定された操作関数を適用する。
    """
    return operation(x, y)

def add(a: int, b: int) -> int:
    return a + b

def subtract(a: int, b: int) -> int:
    return a - b

print(f"加算結果: {apply_operation(add, 10, 5)}")
print(f"減算結果: {apply_operation(subtract, 10, 5)}")

# 5. Any (利用は最小限に)
def log_data(data: Any) -> None:
    """
    どんなデータでもログに出力する関数。
    """
    print(f"ログデータ: {data}, 型: {type(data)}")

log_data("これは文字列です")
log_data(12345)
log_data([1, 2, 3])

型ヒントはコードの品質を向上させる強力なツールですが、強制されるものではありません。プロジェクトの規模やチームの慣習に合わせて、適切に導入を検討してみてください。


こちらのサイトで実際に型をチェックしながら実装しています。

1891年 ヴィクトリア朝のシャーロックホームズの世界に3名の生成AIが突如現れる。
現代社会の不可思議なビジネス課題をこの3名の生成AIが謎に迫る

  • ビジネスコンテンツ × ミステリー × 生成AIの世界観をお楽しみください。

  • ROI探偵事務所の事件で使用した知識をこちらに掲載しています

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?