高校日本史190571 views
高校生物551766 views
数学講師2886227 views
MathPython497343 views
小学算数1200427 views
ヒストリア290598 views
小学理科719870 views
教育149510 views
中学理科1630641 views
LaTeX962153 views
Help
Tools
NewsSpreadsheetCalendarSlidesTier ListPen ToolIllustrationCrayonPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsCross SectionsMotion PathMechanicsWavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic Theory

English

TypedDict で辞書に構造を持たせる

Python の辞書は柔軟で、どんなキーにどんな値でも入れられる。しかし dict[str, Any] では「何でも入る箱」としか表現できず、型チェックの恩恵がほとんど得られない。TypedDict を使えば、キーごとに値の型を指定した「構造を持つ辞書」を定義できる。

TypedDict の基本

TypedDict はクラス構文で辞書のスキーマを定義する。

from typing import TypedDict

class User(TypedDict):
    name: str
    age: int
    email: str

user: User = {"name": "Alice", "age": 30, "email": "alice@example.com"}

見た目はクラスだが、実行時には普通の dict として振る舞う。型チェッカだけがキーと値の型を検証してくれるため、実行時のオーバーヘッドはない。

user: User = {"name": "Alice", "age": "thirty", "email": "alice@example.com"}
# mypy エラー: age は int であるべき

誤った型の値を入れると、mypy が静的にエラーを報告する。実行しなくてもバグを見つけられるのが型ヒントの強みだ。

なぜ dataclass ではなく TypedDict なのか

構造化されたデータを扱うなら dataclass でもよいのでは、と思うかもしれない。TypedDict が必要になるのは、API のレスポンスや JSON データのように「辞書として扱わなければならない」場面だ。

dataclass

独自のクラスとしてインスタンスを生成する。属性アクセスは user.name のようにドット記法。

TypedDict

あくまで dict であり、user["name"] のようにブラケット記法でアクセスする。JSON との相互変換がそのままできる。

外部 API から受け取った JSON を json.loads でパースすると dict が返ってくる。その dict に型を付けたいときに TypedDict がぴたりとはまる。

import json
from typing import TypedDict

class ApiResponse(TypedDict):
    status: int
    message: str
    data: list[str]

raw = '{"status": 200, "message": "OK", "data": ["a", "b"]}'
response: ApiResponse = json.loads(raw)
print(response["status"])   # 200
print(response["message"])  # OK

オプショナルなキー

辞書のキーの中には、存在しないかもしれないものがある。total パラメータで必須キーとオプションキーを分けられる。

from typing import TypedDict

class Config(TypedDict, total=False):
    host: str
    port: int
    debug: bool

config: Config = {"host": "localhost"}  # port と debug は省略可

total=False を指定すると、すべてのキーがオプションになる。一部だけオプションにしたい場合は、必須部分とオプション部分を分けて継承する方法がある。

from typing import TypedDict

class _UserRequired(TypedDict):
    name: str
    age: int

class User(_UserRequired, total=False):
    email: str
    phone: str

user: User = {"name": "Alice", "age": 30}  # email, phone は省略可

Python 3.11 以降では RequiredNotRequired を使ってもっと直感的に書ける。

from typing import TypedDict, Required, NotRequired

class User(TypedDict):
    name: Required[str]
    age: Required[int]
    email: NotRequired[str]
    phone: NotRequired[str]
total=False による制御

全キーを一括でオプションにする。一部だけオプションにするには継承が必要。

Required / NotRequired

Python 3.11 以降で利用可能。キーごとに必須・オプションを個別に指定できる。

ネストした TypedDict

辞書の中に辞書がある構造も、TypedDict を入れ子にして表現できる。

from typing import TypedDict

class Address(TypedDict):
    city: str
    zip_code: str

class Company(TypedDict):
    name: str
    address: Address

company: Company = {
    "name": "Acme Corp",
    "address": {"city": "Tokyo", "zip_code": "100-0001"},
}

ネストが深い JSON レスポンスを扱うとき、各レベルを TypedDict で定義しておけば、どの階層でも型チェックが効く。

関数の引数としての TypedDict

TypedDict は関数の引数や戻り値にも使える。

from typing import TypedDict

class SearchParams(TypedDict):
    query: str
    page: int
    per_page: int

def search(params: SearchParams) -> list[str]:
    print(f"Searching: {params['query']} (page {params['page']})")
    return []

search({"query": "python", "page": 1, "per_page": 20})

辞書をそのまま渡す API では、引数に TypedDict を指定することで「どのキーが必要か」を呼び出し側に明示できる。キーの typo も型チェッカが検出してくれる。

実行時の振る舞い

TypedDict は型チェッカ向けの仕組みであり、実行時にはただの dict として扱われる。isinstance チェックで TypedDict かどうかを判定することはできない。

from typing import TypedDict

class User(TypedDict):
    name: str
    age: int

user: User = {"name": "Alice", "age": 30}
print(type(user))           # <class 'dict'>
print(isinstance(user, dict))  # True

実行時のバリデーションが必要なら、pydantic や cattrs のようなライブラリと組み合わせることになる。TypedDict はあくまで静的解析のためのツールであり、実行時の安全性は別の仕組みで担保する必要がある。この割り切りが TypedDict の特徴でもあり、限界でもある。