Skip to content

Python型ヒント: 型注釈の実践ガイド

Updated on

Pythonの動的型付けは柔軟性に優れていますが、大規模になれば実際の問題を引き起こします。関数は誤った型を黙って受け入れ、実際のバグから遠い場所で混乱したエラーを発生させます。リファクタリングは予測不可能な箇所でコードを破壊します。他の人のコードを読む際には、各変数、各関数パラメータ、各戻り値を通過する型を推測する必要があります。コードベースが成長するにつれ、これらの推測はバグに変わり、そのバグはデバッグ時間に変わっていきます。

チーム環境ではこのコストがさらに増大します。型情報がないと、関数呼び出しごとに実装を読んで何を期待し何を返すかを理解する必要があります。コードレビューは遅くなります。新しいチームメンバーのオンボーディングには時間がかかります。自動化ツールは動作するための型情報がないため助けになることができません。

Pythonの型ヒントは、IDE、型チェッカー、人間が検証できるオプションの型注釈を追加することでこれを解決します。意図をコードに直接文書化し、実行前にバグのカテゴリー全体を捕捉し、オートコンプリートやインラインエラー検出などの強力なエディタ機能を解放します。このガイドでは、基本的な注釈から本番のPythonコードベースで使用される高度なパターンまで、すべてをカバーします。

型ヒントとは何か?

型ヒントは、変数、関数パラメータ、戻り値の期待される型を指定するオプションの注釈です。PEP 484 (opens in a new tab)(Python 3.5)で導入され、PEP 526(変数注釈)、PEP 604(ユニオン構文)、PEP 612(パラメータ仕様)などの後続のPEPによって改良されてきました。

重要なキーワードはオプションです。型ヒントは実行時の動作には影響しません。Pythonは実行中にそれらを強制しません。それらは3つの対象者のために存在します:コードを読む開発者、オートコンプリートやエラー検出を提供するIDE、そしてコードを実行せずに分析するmypyのような静的型チェッカーです。

# 型ヒントなし - この関数は何を期待しているか?
def process_data(data, threshold, output):
    ...
 
# 型ヒントあり - 一目瞭然
def process_data(data: list[float], threshold: float, output: str) -> dict[str, float]:
    ...

2番目のバージョンは一目ですべてを伝えます:dataはfloatのリスト、thresholdはfloat、outputは文字列で、関数は文字列からfloatへの辞書を返します。実装を読んだり呼び出し元を辿ったりする必要はありません。

基本的な型注釈

変数注釈

変数注釈は、PEP 526(Python 3.6)で導入されたコロン構文を使用します:

# 基本的な変数注釈
name: str = "Alice"
age: int = 30
height: float = 5.9
is_active: bool = True
raw_data: bytes = b"hello"
 
# 代入なしの注釈(宣言のみ)
username: str
count: int

値を代入せずに変数に注釈を付けることができます。これは、後で変数が代入されるクラス本体や条件ブロックで役立ちます。

関数パラメータと戻り値の型

関数注釈は、パラメータにはコロンを、戻り値には->を使用します:

def greet(name: str) -> str:
    return f"Hello, {name}!"
 
def calculate_average(numbers: list[float]) -> float:
    return sum(numbers) / len(numbers)
 
def save_record(record: dict[str, str], overwrite: bool = False) -> None:
    """意味のある値を返さずアクションを実行する関数は -> None を使用します。"""
    ...

-> None注釈は、関数が意味のある値を返さずにアクションを実行することを明示的に伝えます。これは、意図的なNoneの戻りと、忘れられたreturn文を区別するため重要です。

組み込み型

Pythonの組み込み型は型ヒントに直接マッピングされます:

説明
intcount: int = 10整数
floatprice: float = 9.99浮動小数点数
strname: str = "Bob"文字列
boolactive: bool = Trueブール値
bytesdata: bytes = b"\x00"バイト列
Noneresult: None = NoneNoneシングルトン
def parse_config(path: str, encoding: str = "utf-8") -> dict[str, str]:
    config: dict[str, str] = {}
    with open(path, encoding=encoding) as f:
        for line in f:
            key, _, value = line.partition("=")
            config[key.strip()] = value.strip()
    return config

コレクション型

モダン構文(Python 3.9+)

Python 3.9以降では、組み込みのコレクション型をジェネリック型として直接使用できます:

# リスト
scores: list[int] = [95, 87, 92]
names: list[str] = ["Alice", "Bob"]
 
# 辞書
user_ages: dict[str, int] = {"Alice": 30, "Bob": 25}
config: dict[str, list[str]] = {"servers": ["a.com", "b.com"]}
 
# タプル - 固定長で特定の型
point: tuple[float, float] = (3.14, 2.72)
record: tuple[str, int, bool] = ("Alice", 30, True)
 
# 可変長タプル(すべて同じ型)
values: tuple[int, ...] = (1, 2, 3, 4, 5)
 
# セットとfrozenset
tags: set[str] = {"python", "typing"}
constants: frozenset[int] = frozenset({1, 2, 3})

レガシー構文(Python 3.5-3.8)

Python 3.9より前は、typingモジュールからジェネリック型をインポートする必要がありました:

from typing import List, Dict, Tuple, Set, FrozenSet
 
scores: List[int] = [95, 87, 92]
user_ages: Dict[str, int] = {"Alice": 30}
point: Tuple[float, float] = (3.14, 2.72)
tags: Set[str] = {"python", "typing"}

構文比較表

Python 3.9+Python 3.5-3.8
リストlist[int]typing.List[int]
辞書dict[str, int]typing.Dict[str, int]
タプル(固定)tuple[str, int]typing.Tuple[str, int]
タプル(可変)tuple[int, ...]typing.Tuple[int, ...]
セットset[str]typing.Set[str]
FrozenSetfrozenset[int]typing.FrozenSet[int]
type[MyClass]typing.Type[MyClass]

プロジェクトがPython 3.9以降を対象とする場合は、常にモダン構文を使用してください。よりクリーンで、インポートも不要です。

ネストしたコレクション

コレクション型は、複雑なデータ構造のために自然に合成されます:

# マトリックス: floatのリストのリスト
matrix: list[list[float]] = [[1.0, 2.0], [3.0, 4.0]]
 
# ユーザーとそのスコアリストのマッピング
gradebook: dict[str, list[int]] = {
    "Alice": [95, 87, 92],
    "Bob": [78, 85, 90],
}
 
# 設定: ネストした辞書
app_config: dict[str, dict[str, str | int]] = {
    "database": {"host": "localhost", "port": 5432},
    "cache": {"host": "redis.local", "port": 6379},
}

OptionalとUnion型

Optional型

Noneになりうる値は、Optionalまたはユニオン構文で注釈されます:

from typing import Optional
 
# 3.10以前の構文
def find_user(user_id: int) -> Optional[str]:
    """ユーザー名を返すか、見つからない場合はNoneを返します。"""
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)
 
# Python 3.10+ 構文(推奨)
def find_user(user_id: int) -> str | None:
    """ユーザー名を返すか、見つからない場合はNoneを返します。"""
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

Optional[str]str | Noneと完全に等価です。パイプ構文はより読みやすく、インポートも不要です。

Union型

値がいくつかの型のいずれかである場合:

from typing import Union
 
# 3.10以前の構文
def format_value(value: Union[int, float, str]) -> str:
    return str(value)
 
# Python 3.10+ 構文(推奨)
def format_value(value: int | float | str) -> str:
    return str(value)
 
# 一般的なパターン: 複数の入力形式を受け入れる
def load_data(source: str | Path) -> list[dict[str, str]]:
    path = Path(source) if isinstance(source, str) else source
    with open(path) as f:
        return json.load(f)

Union構文の比較

パターン3.10以前Python 3.10+
NullableOptional[str]str | None
2つの型Union[int, str]int | str
複数Union[int, float, str]int | float | str
Nullable unionOptional[Union[int, str]]int | str | None

typingモジュールの高度な型

Any

Anyは、特定の値の型チェックを無効にします。エスケープハッチとして控えめに使用してください:

from typing import Any
 
def log_event(event: str, payload: Any) -> None:
    """あらゆるペイロード型を受け入れます - 汎用ロギングに有用です。"""
    print(f"[{event}] {payload}")
 
# Anyはすべての型と互換性があります
log_event("click", {"x": 100, "y": 200})
log_event("error", 404)
log_event("message", "hello")

TypeVarとGeneric

TypeVarは、型関係を保持しながら複数の型で動作する関数やクラスのためのジェネリック型変数を作成します:

from typing import TypeVar
 
T = TypeVar("T")
 
def first_element(items: list[T]) -> T:
    """最初の要素を返し、その型を保持します。"""
    return items[0]
 
# 型チェッカーは正しい戻り値の型を推論します
name = first_element(["Alice", "Bob"])     # type: str
score = first_element([95, 87, 92])        # type: int
 
# 境界付きTypeVar - 特定の型に制限
Numeric = TypeVar("Numeric", int, float)
 
def add(a: Numeric, b: Numeric) -> Numeric:
    return a + b

ジェネリッククラスの作成:

from typing import TypeVar, Generic
 
T = TypeVar("T")
 
class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []
 
    def push(self, item: T) -> None:
        self._items.append(item)
 
    def pop(self) -> T:
        return self._items.pop()
 
    def peek(self) -> T:
        return self._items[-1]
 
    def is_empty(self) -> bool:
        return len(self._items) == 0
 
# 特定の型での使用
int_stack: Stack[int] = Stack()
int_stack.push(42)
value: int = int_stack.pop()
 
str_stack: Stack[str] = Stack()
str_stack.push("hello")

Callable

Callableは関数パラメータとコールバックに注釈を付けます:

from typing import Callable
 
# コールバックを取る関数
def apply_operation(
    values: list[float],
    operation: Callable[[float], float]
) -> list[float]:
    return [operation(v) for v in values]
 
# 使用例
import math
results = apply_operation([1.0, 4.0, 9.0], math.sqrt)
# results: [1.0, 2.0, 3.0]
 
# より複雑なcallableシグネチャ
Comparator = Callable[[str, str], int]
EventHandler = Callable[[str, dict[str, str]], None]
 
def sort_with_comparator(
    items: list[str],
    compare: Comparator
) -> list[str]:
    import functools
    return sorted(items, key=functools.cmp_to_key(compare))

Literal

Literalは値を特定の定数に制限します:

from typing import Literal
 
def set_log_level(level: Literal["DEBUG", "INFO", "WARNING", "ERROR"]) -> None:
    print(f"Log level set to {level}")
 
set_log_level("INFO")      # OK
set_log_level("VERBOSE")   # 型エラー: 有効なリテラルではありません
 
# モードパラメータに有用
def read_file(
    path: str,
    mode: Literal["text", "binary"] = "text"
) -> str | bytes:
    if mode == "text":
        return open(path).read()
    return open(path, "rb").read()

TypedDict

TypedDictは、特定のキーと値の型を持つ辞書の形状を定義します:

from typing import TypedDict, NotRequired
 
class UserProfile(TypedDict):
    name: str
    email: str
    age: int
    bio: NotRequired[str]  # オプションのキー (Python 3.11+)
 
def display_user(user: UserProfile) -> str:
    return f"{user['name']} ({user['email']}), age {user['age']}"
 
# 型チェッカーは構造を検証します
user: UserProfile = {
    "name": "Alice",
    "email": "alice@example.com",
    "age": 30,
}
 
display_user(user)  # OK

Protocol(構造的型付け)

Protocolは、継承ではなく構造に基づいてインターフェースを定義します。オブジェクトが必要なメソッドを持っていれば、そのプロトコルを満たします:

from typing import Protocol, runtime_checkable
 
@runtime_checkable
class Drawable(Protocol):
    def draw(self, x: int, y: int) -> None: ...
 
class Circle:
    def draw(self, x: int, y: int) -> None:
        print(f"Drawing circle at ({x}, {y})")
 
class Square:
    def draw(self, x: int, y: int) -> None:
        print(f"Drawing square at ({x}, {y})")
 
def render(shape: Drawable, x: int, y: int) -> None:
    shape.draw(x, y)
 
# Drawableから継承しなくても両方とも動作します
render(Circle(), 10, 20)
render(Square(), 30, 40)
 
# runtime_checkableによりisinstanceチェックが可能になります
print(isinstance(Circle(), Drawable))  # True

TypeAlias

TypeAliasは、複雑な型の明示的な型エイリアスを作成します:

from typing import TypeAlias
 
# 単純なエイリアス
UserId: TypeAlias = int
JsonDict: TypeAlias = dict[str, "JsonValue"]
JsonValue: TypeAlias = str | int | float | bool | None | list["JsonValue"] | JsonDict
 
# 複雑なエイリアスはシグネチャを簡潔にします
Matrix: TypeAlias = list[list[float]]
Callback: TypeAlias = Callable[[str, int], bool]
Config: TypeAlias = dict[str, str | int | list[str]]
 
def transform_matrix(m: Matrix, factor: float) -> Matrix:
    return [[cell * factor for cell in row] for row in m]
 
# Python 3.12+ ではtype文を使用します
# type Vector = list[float]

mypyによる型チェック

mypyのインストールと実行

mypyはPythonで最も広く使用されている静的型チェッカーです:

# mypyのインストール
# pip install mypy
 
# 単一ファイルのチェック
# mypy script.py
 
# プロジェクト全体のチェック
# mypy src/
 
# 特定のPythonバージョンでチェック
# mypy --python-version 3.10 src/

設定

pyproject.tomlまたはmypy.iniでプロジェクト全体の設定を構成します:

# pyproject.tomlの設定
# [tool.mypy]
# python_version = "3.10"
# warn_return_any = true
# warn_unused_configs = true
# disallow_untyped_defs = true
# check_untyped_defs = true
# no_implicit_optional = true
# strict_equality = true
 
# モジュールごとのオーバーライド
# [[tool.mypy.overrides]]
# module = "third_party_lib.*"
# ignore_missing_imports = true

一般的なmypyフラグ

フラグ効果
--strictすべての厳格なチェックを有効化(新規プロジェクトで推奨)
--ignore-missing-imports型付けされていないサードパーティライブラリのエラーをスキップ
--disallow-untyped-defsすべての関数に型注釈を必須にする
--no-implicit-optionalNoneのデフォルトをOptionalとして扱わない
--warn-return-any型付けされた関数からAnyを返す場合に警告
--show-error-codes各エラーのエラーコードを表示(抑制に有用)

一般的なmypyエラーの修正

# エラー: 互換性のない戻り値の型 (got "Optional[str]", expected "str")
# 修正: Noneの場合を処理する
def get_name(user_id: int) -> str:
    result = lookup(user_id)  # str | None を返す
    if result is None:
        raise ValueError(f"User {user_id} not found")
    return result  # mypyはresultがstrであることを認識します
 
# エラー: "Optional[str]"の項目"None"に属性"upper"がありません
# 修正: まず型を絞り込む
def format_name(name: str | None) -> str:
    if name is not None:
        return name.upper()
    return "UNKNOWN"
 
# エラー: 変数の型注釈が必要です
# 修正: 明示的な注釈を追加
items: list[str] = []  # 単なる items = [] ではなく
 
# エラー: 代入における互換性のない型
# 修正: Unionを使用するか型を修正
value: int | str = 42
value = "hello"  # union型ではOK
 
# 必要に応じて特定のエラーを抑制
x = some_untyped_function()  # type: ignore[no-untyped-call]

実践での型ヒント

FastAPIとPydantic

FastAPIは型ヒントを使用して、リクエストの検証、シリアライゼーション、およびドキュメントを実現します:

from fastapi import FastAPI
from pydantic import BaseModel
 
app = FastAPI()
 
class UserCreate(BaseModel):
    name: str
    email: str
    age: int
    tags: list[str] = []
 
class UserResponse(BaseModel):
    id: int
    name: str
    email: str
 
@app.post("/users", response_model=UserResponse)
async def create_user(user: UserCreate) -> UserResponse:
    # FastAPIはリクエストボディをUserCreateに対して検証し
    # レスポンスをUserResponseに合わせてシリアライズします
    return UserResponse(id=1, name=user.name, email=user.email)

Pydanticは型ヒントを使用して、データの自動検証、型変換、およびJSONスキーマの生成を行います。型注釈はドキュメントだけでなく、実行時の動作を駆動します。

データサイエンス: DataFrameの型付け

型ヒントはデータサイエンスワークフローでますます重要になっています:

import pandas as pd
from typing import Any
 
# 基本的なDataFrameの型付け
def clean_dataframe(df: pd.DataFrame) -> pd.DataFrame:
    return df.dropna().reset_index(drop=True)
 
# panderaによるスキーマ検証の使用
# pip install pandera
import pandera as pa
from pandera.typing import DataFrame, Series
 
class SalesSchema(pa.DataFrameModel):
    product: Series[str]
    quantity: Series[int] = pa.Field(ge=0)
    price: Series[float] = pa.Field(gt=0)
    date: Series[pd.Timestamp]
 
@pa.check_types
def process_sales(df: DataFrame[SalesSchema]) -> DataFrame[SalesSchema]:
    """型チェックされたDataFrame処理。"""
    return df[df["quantity"] > 0]

IDEのメリット

型ヒントは、すべての主要なエディタで強力なIDE機能のロックを解除します:

機能型ヒントなし型ヒントあり
オートコンプリート一般的な候補正確な型のコンテキストに応じた候補
エラー検出実行時エラーのみ実行前のインラインエラー
リファクタリング手動の検索と置換安全な自動名前変更とリファクタリング
ドキュメントdocstringやソース読み込みが必要ホバーで型をインライン表示
ナビゲーションテキスト検索型付けされた定義と実装へのジャンプ

ツール横断的な型ヒントのメリット

ツール型ヒントの使用方法
mypy静的型チェック、実行前のバグ捕捉
Pyright/PylanceVS Codeでの型チェックとオートコンプリート
FastAPIリクエスト/レスポンスの検証とAPIドキュメント
Pydanticデータ検証、シリアライゼーション、設定管理
SQLAlchemy 2.0マッピングされたカラム、クエリ結果の型
pytestプラグインの型推論、フィクスチャの型付け
attrs/dataclasses自動__init____repr____eq__生成

型ヒントチートシート

ここに最も一般的な型注釈のクイックリファレンス表があります:

注釈意味
int整数count: int = 0
float浮動小数点数price: float = 9.99
str文字列name: str = "Alice"
boolブール値active: bool = True
bytesバイトdata: bytes = b""
NoneNone型-> None
list[int]intのリストscores: list[int] = []
dict[str, int]strからintへの辞書ages: dict[str, int] = {}
tuple[str, int]固定長タプルpair: tuple[str, int]
tuple[int, ...]可変長タプルnums: tuple[int, ...]
set[str]文字列のセットtags: set[str] = set()
str | NoneNullable文字列 (3.10+)name: str | None = None
Optional[str]Nullable文字列 (3.10以前)name: Optional[str] = None
int | strintまたは文字列 (3.10+)value: int | str
Union[int, str]intまたは文字列 (3.10以前)value: Union[int, str]
Any任意の型(エスケープハッチ)data: Any
Callable[[int], str]関数型fn: Callable[[int], str]
Literal["a", "b"]特定の値のみmode: Literal["r", "w"]
TypeVar("T")ジェネリック型変数T = TypeVar("T")
ClassVar[int]クラスレベル変数count: ClassVar[int] = 0
Final[str]再代入不可NAME: Final = "app"
TypeAlias明示的な型エイリアスUserId: TypeAlias = int

よくある間違い

間違い問題修正
def f(x: list)要素型が欠落def f(x: list[int])
items = []型を推論できないitems: list[str] = []
def f(x: int = None)デフォルトはNoneだが型はintdef f(x: int | None = None)
from typing import List (3.9+)不要なインポートlist[int]を直接使用
def f(x: dict)キー/値の型が欠落def f(x: dict[str, int])
isinstance(x, list[int])ジェネリクスをisinstanceで使用不可isinstance(x, list)
def f() -> True値を使用しているが型ではないdef f() -> bool
selfに注釈冗長、mypyは推論するselfの注釈を省略
x: str = 42間違った注釈注釈を実際の型に合わせる
Anyの過度の使用型付けの目的を損なう具体的な型またはTypeVarを使用
# 間違い: 変更可能なデフォルト値に型ヒント
def bad_append(item: str, items: list[str] = []) -> list[str]:
    items.append(item)  # 共有される変更可能なデフォルト!
    return items
 
# 修正: Noneをデフォルトとして使用
def good_append(item: str, items: list[str] | None = None) -> list[str]:
    if items is None:
        items = []
    items.append(item)
    return items

実践例: 型付きデータパイプライン

ここに、型ヒントが実際のデータ処理シナリオでどのように連携するかを示す完全な例があります。このパターンはデータサイエンスや分析ワークフローで一般的です:

from typing import TypedDict, Callable, TypeAlias
from pathlib import Path
import csv
 
# TypedDictでデータ形状を定義
class RawRecord(TypedDict):
    name: str
    value: str
    category: str
 
class ProcessedRecord(TypedDict):
    name: str
    value: float
    category: str
    normalized: float
 
# 変換関数の型エイリアス
Transform: TypeAlias = Callable[[list[ProcessedRecord]], list[ProcessedRecord]]
 
def load_csv(path: Path) -> list[RawRecord]:
    """型付き出力でCSVデータを読み込みます。"""
    records: list[RawRecord] = []
    with open(path) as f:
        reader = csv.DictReader(f)
        for row in reader:
            records.append(RawRecord(
                name=row["name"],
                value=row["value"],
                category=row["category"],
            ))
    return records
 
def parse_records(raw: list[RawRecord]) -> list[ProcessedRecord]:
    """生の文字列レコードを型付きレコードに変換します。"""
    max_value = max(float(r["value"]) for r in raw) or 1.0
    return [
        ProcessedRecord(
            name=r["name"],
            value=float(r["value"]),
            category=r["category"],
            normalized=float(r["value"]) / max_value,
        )
        for r in raw
    ]
 
def filter_by_category(
    records: list[ProcessedRecord],
    category: str
) -> list[ProcessedRecord]:
    """レコードをフィルタリングし、入力と出力を完全に型付けします。"""
    return [r for r in records if r["category"] == category]
 
def apply_transforms(
    records: list[ProcessedRecord],
    transforms: list[Transform]
) -> list[ProcessedRecord]:
    """型付き変換関数のチェーンを適用します。"""
    result = records
    for transform in transforms:
        result = transform(result)
    return result
 
# 使用例
raw = load_csv(Path("data.csv"))
processed = parse_records(raw)
filtered = filter_by_category(processed, "electronics")

このパイプラインのすべての関数には、明確な入力型と出力型があります。型チェッカーは、ある関数の出力が次の関数の入力と一致するか検証できます。ProcessedRecord構造を変更した場合、mypyは更新が必要なすべての場所をフラグします。

PyGWalkerによる型付きデータの可視化

データサイエンスワークフローで型付きDataFrameを扱う際、PyGWalker (opens in a new tab)はpandasのDataFrameをインタラクティブなTableauライクな可視化インターフェースに変換します。適切な型付けによって生成された構造化データと連携して、探索可能なチャートやダッシュボードに直接フィードされます:

import pandas as pd
import pygwalker as pyg
 
# 型付きパイプラインがクリーンで構造化されたデータを生成
data: list[ProcessedRecord] = parse_records(raw)
df = pd.DataFrame(data)
 
# PyGWalkerがインタラクティブな可視化としてレンダリング
walker = pyg.walk(df)

インタラクティブなノートブック環境では、RunCell (opens in a new tab)は、型チェックされたコードと視覚的なデータ探索がシームレスに連携するAI駆動のJupyter体験を提供します。

FAQ

型ヒントはPythonのパフォーマンスに影響しますか?

いいえ。Pythonのランタイムは型ヒントを完全に無視します。関数や変数にメタデータとして保存されますが、通常の実行中は評価されません。注釈を保存するためのわずかなメモリオーバーヘッドがありますが、実行速度への影響はありません。PydanticやFastAPIのようなフレームワークは、検証ロジックを構築するために起動時に注釈を読み取りますが、これはフレームワークの動作であり、Python言語機能ではありません。

Pythonで型ヒントは必須ですか?

いいえ。型ヒントは完全にオプションです。Pythonは動的型付け言語であり、注釈の有無にかかわらず同じように実行されます。ただし、型ヒントは複数の開発者がいるプロジェクトや長期的なメンテナンスが必要なコードベースに強く推奨されています。FastAPI、SQLAlchemy、Djangoなどの主要なPythonプロジェクトは、ますます型ヒントに依存しています。

型ヒントと型チェックの違いは何ですか?

型ヒントはコードに書く注釈(x: int-> strなど)です。型チェックは、コードがこれらの注釈と一致しているか検証するプロセスです。型チェックは、mypy、Pyright、Pylanceのような外部ツールによって実行され、Python自体によって行われるわけではありません。型チェッカーを実行せずに型ヒントを持つことは可能ですし、それでもIDEのオートコンプリートやドキュメントを通じて価値を提供します。

型ヒントにはtyping.Listとlistのどちらを使用すべきですか?

プロジェクトがPython 3.9以降を対象とする場合は、小文字のlist[int]を使用してください。大文字のtyping.List[int]はPython 3.5-3.8で必要なレガシー構文です。小文字構文はよりクリーンで、インポートも不要であり、今後推奨されるアプローチです。dicttyping.Dicttupletyping.Tuplesettyping.Setも同様です。

Pythonで最適な型チェッカーは何ですか?

mypyは、最も確立されて広く使用されているPythonの型チェッカーです。Pyright(VS CodeのPylance拡張機能で使用)は高速で、mypyが見逃す一部のエラーを捕捉します。両方とも積極的にメンテナンスされています。ほとんどのプロジェクトでは、エディタとの統合が最も優れたものを使用してください。mypyはCIパイプラインの標準です。PyrightはVS Codeで最優秀のリアルタイム体験を提供します。両方を競合なく同じプロジェクトで実行できます。

まとめ

Pythonの型ヒントは、Pythonの動的柔軟性と静的型付け言語の安全性の保証との間のギャップを埋めます。実行前にバグを捕捉し、コードを自己文書化し、開発を高速化する強力なIDE機能のロックを解除します。

基本から始めましょう:関数パラメータ、戻り値の型、複雑な変数に注釈を付けます。日常の型にはlist[int]dict[str, str]str | Noneを使用します。CIパイプラインでmypyを実行して、型エラーを自動的に捕捉します。自信がついたら、TypedDictProtocolGenericのような高度なパターンを採用して、複雑なドメイン型をモデル化します。

投資はすぐに報われます。本番バグを防ぐ単一の型注釈が、何時間もの入力作業を正当化します。チームは、型ヒントの採用後、より迅速なオンボーディング、より安全なリファクタリング、より少ない実行時の予期せぬ事態を報告しています。FastAPIやPydanticのようなフレームワークが型注釈を中心に設計を構築していることから、型付きPythonはニッチな実践ではなく、エコシステムが進む方向なのです。

📚