Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Python型ヒント完全ガイド 初心者でも分かる、現代的で実践的な使い方

Python型ヒント完全ガイド 初心者でも分かる、現代的で実践的な使い方

Python型ヒント完全ガイド

初心者でも分かる、現代的で実践的な使い方

Avatar for MIKIO KUBO

MIKIO KUBO

July 03, 2025
Tweet

More Decks by MIKIO KUBO

Other Decks in Programming

Transcript

  1. 1. 型ヒントの「なぜ」 Python の動的型付け:諸刃の剣 Pythonは 動的型付け言語です。 利点: 柔軟で、素早くコードが書ける。 欠点: 大規模になると、予期せぬ型のエラーが実行時までわからない。

    def add_numbers(a, b): return a + b print(add_numbers(5, 10)) # -> 15 (OK) print(add_numbers("a", "b")) # -> "ab" (OK?) # 実行して初めてエラーが発覚! # print(add_numbers(5, "hello")) # TypeError: unsupported operand type(s) 2
  2. mypy のインストールと準備 プロジェクトごとに仮想環境を作成し、その中で mypy をインストールするのがベストプラクティス です。 # 1. 仮想環境を作成 python

    -m venv .venv # 2. 仮想環境を有効化 # Windows: .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate # 3. mypyをインストール pip install mypy 6
  3. mypy を使ってみよう mypy がどのようにエラーを見つけるか見てみましょう。 app.py # app.py def greet(name: str)

    -> str: return "Hello, " + name print(greet("World")) print(greet(42)) # ここで型が違う! ターミナルで実行 mypy app.py 7
  4. mypy のフィードバックを理解する mypy は、潜在的なバグを正確に指摘してくれます。 エラーメッセージ app.py:5: error: Argument 1 to

    "greet" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) app.py:5 : ファイル名と行番号 Argument 1 to "greet" : greet 関数の第一引数で incompatible type "int" : int 型が渡されているが expected "str" : str 型が期待されていた → コードを実行する前に、問題を発見できました! 8
  5. 3. アノテーションの基礎 型を記述するための基本的な構文を学びます。 変数のアノテーション 変数名: 型 = 値 の構文を使います。 #

    整数型 (integer) user_id: int = 101 # 文字列型 (string) user_name: str = "Alice" # 浮動小数点数型 (float) temperature: float = 36.5 # ブーリアン型 (boolean) is_active: bool = True 9
  6. 関数のアノテーション 引数と戻り値に型を付けます。 def 関数名(引数: 型) -> 戻り値の型: def get_user_summary(user_id: int,

    is_premium: bool) -> str: """ ユーザーIDとプレミアム状態から要約文字列を生成する。 """ status = "premium" if is_premium else "standard" return f"User {user_id} is a {status} user." # mypyはこの使い方が正しいかチェックできる summary = get_user_summary(101, True) 10
  7. 値を返さない関数:-> None 関数が明示的に値を返さない場合、戻り値の型として None を使います。 (他の言語の void に相当します) def print_report(message:

    str) -> None: """ メッセージをコンソールに出力する。 この関数は値を返さない。 """ print(f"REPORT: {message}") result = print_report("System status: OK") print(result) # 出力: None -> None は「この関数は副作用のためにあり、戻り値を使うべきではない」という設計意図を示し ます。 11
  8. 4. コレクションの型付け リストや辞書など、複数の値をまとめる型の付け方です。 モダンな方法 (Python 3.9+) list , dict などの組み込み型を直接使えます。

    これが現在の標準です。 # 文字列のリスト names: list[str] = ["Alice", "Bob", "Charlie"] # 文字列をキー、整数を値に持つ辞書 user_ages: dict[str, int] = {"Alice": 30, "Bob": 25} # 整数のセット unique_ids: set[int] = {101, 102, 103} 簡潔で直感的なため、新しいコードではこちらを使いましょう。 12
  9. tuple の特殊なケース tuple は2つの使われ方があります。 1. 可変長の同種タプル 全ての要素が同じ型。 # 任意の数の整数を含むタプル scores:

    tuple[int, ...] = (95, 88, 100) 2. 固定長の異種タプル 要素ごとに型が異なる「レコード」のような構造。(こちらが一般的) # (ID, 名前, アクティブ状態) を表現 user_record: tuple[int, str, bool] = (101, "Alice", True) 13
  10. コレクション型ヒントの比較表 説明 モダンな構文 (Python 3.9+) 旧来の構文 (Python < 3.9) 整数のリスト

    list[int] from typing import List List[int] 辞書(strキー, float値) dict[str, float] from typing import Dict Dict[str, float] 文字列のセット set[str] from typing import Set Set[str] 固定長タプル tuple[str, int] from typing import Tuple Tuple[str, int] 可変長タプル tuple[int, ...] from typing import Tuple Tuple[int, ...] 14
  11. 5. None の様々な表現方法 「値が存在しない可能性」を示す方法。これは初心者が混乱しやすいポイントです。 新標準 (Python 3.10+) :| 演算子 |

    (Union Operator) を使うのが、最もモダンで推奨される方法です。 # 文字列またはNoneになりうる変数 user_nickname: str | None = None # ユーザーを見つけ、見つかればID(int)を、なければNoneを返す def find_user_id(username: str) -> int | None: if username in user_database: return user_database[username] return None str または None という意図が直接的に表現できて、非常に読みやすいです。 15
  12. 過去の方法:Union と Optional | 演算子が導入される前は、 typing モジュールの型が使われていました。 Union str |

    None は Union[str, None] と完全に等価です。 from typing import Union user_nickname: Union[str, None] = None Optional ( 注意!) Optional[str] は Union[str, None] のショートカットですが、 名前が誤解を招きやすいです。 誤解: 引数が省略可能(オプショナル) 正解: 値として None を許容するだけ 現在、新しいコードでの Optional の使用は推奨されていません。 16
  13. どの構文を選ぶべきか? 明確な推奨があります。 1. Python 3.10 以降: 常に | 演算子を使いましょう。 str

    | None 2. Python 3.9 以前: Union を使うのが最も明確です。 Union[str, None] 3. Optional は?: 古いコードで目にしますが、新規に書くのは避けましょう。 構文 Python バージョン 推奨度 `str None` 3.10+ Union[str, None] 3.6+ 良い (3.10未満の標準) Optional[str] 3.6+ 非推奨 (誤解を招く) 17
  14. 6. 型の語彙を増やす より複雑な状況に対応するための高度な型。 「何でもあり」の型:Any Any は、すべての型と互換性があると見なされる特別な型です。 Any を使うとその部分の 型チェックが実質的に無効になります。 型ヒントシステムからの「一時的な避難ハッチ」です。

    from typing import Any # mypyはこの関数内の型チェックをほとんど行わない def process_untyped_data(data: Any) -> Any: # dataが辞書である保証はないが、エラーにならない key = data["key"] return key + 5 注意: Any の乱用は型ヒントの利点を損なうため、必要最小限にしましょう。 18
  15. ジェネリック関数を作る:TypeVar 様々な型に対して一貫した動作をするジェネリックな関数を作れます。 from typing import TypeVar, Sequence # 'T'という名前の型変数を宣言 T

    = TypeVar('T') # Sequence[T] を受け取り、T を返す関数 def get_first_element(items: Sequence[T]) -> T: return items[0] # mypyは型変数の関係性を理解する first_str = get_first_element(["a", "b"]) # first_str は str 型 first_num = get_first_element([1, 2]) # first_num は int 型 TypeVar は、具体的な型に縛られない、型安全で再利用可能なコードを可能にします。 19
  16. 関数を型付けする:Callable 関数を引数として受け取る、または返す高階関数を型付けします。 構文: Callable[[引数1の型, ...], 戻り値の型] from typing import Callable

    # op引数は、2つのintを受け取りintを返す関数 def calculator(x: int, y: int, op: Callable[[int, int], int]) -> int: return op(x, y) def add(a: int, b: int) -> int: return a + b # `add`関数が指定のCallableと一致するかmypyがチェック result = calculator(10, 5, add) # resultはint型 20
  17. 7. 実用的なパターン 実際のコードで役立つ、より高度な型付けパターン。 辞書の構造を定義する:TypedDict キーが固定で、値の型が異なる辞書(例: JSONデータ)に「型」を定義します。 from typing import TypedDict

    # 'User'という辞書の型を定義 class User(TypedDict): id: int name: str is_active: bool def process_user(user: User) -> None: print(f"Processing user: {user['name'].upper()}") user_data: User = {"id": 101, "name": "Alice", "is_active": True} process_user(user_data) # OK # mypyがエラーを検出 -> error: Missing key "is_active" ... # process_user({"id": 102, "name": "Bob"}) 21
  18. 値を特定のものに限定する:Literal 変数が取りうる値を、いくつかの具体的なリテラル値に限定します。 from typing import Literal # align引数は"left", "center", "right"のいずれか

    def set_text_alignment(align: Literal["left", "center", "right"]) -> None: # ... pass set_text_alignment("center") # OK # mypyがエラーを検出 -> error: Argument ... has incompatible type # set_text_alignment("top") 文字列や数値による「モード指定」などでタイポを防ぎ、コードを安全にします。 22
  19. 読みやすさを向上させる:型エイリアス 複雑な型に別名(エイリアス)を付けて、可読性を向上させます。 # 型エイリアスを使わない場合... 読みにくい def process_data(data: list[dict[str, tuple[int, str]]])

    -> None: ... # 型エイリアスを使う UserID = int UserRecord = tuple[UserID, str] UserData = dict[str, UserRecord] UserList = list[UserData] # エイリアスでアノテーションすると、意図が明確に! def process_data_aliased(data: UserList) -> None: ... ドメイン固有のデータ構造を表現する語彙としても役立ちます。 23
  20. 型付けの旅、次のステップへ 1. 自身のプロジェクトに適用する 小さな関数から型ヒントを付けてみよう! 2. スタブファイル ( .pyi ) を探る

    型ヒントがない外部ライブラリも、 types-* パッケージで型チェック可能に。(例: pip install types-requests ) 3. 公式ドキュメントを参照する typing モジュールや mypy のドキュメントには、さらに高度な機能が満載です。 25