第2章:モダン構文
更新日:2025年12月9日
1. 型ヒントと静的解析
1.1 typing基礎
型ヒント(Type Hints)はPEP 484で導入され、Python 3.5以降で利用可能である[1]。型ヒントは実行時には無視されるが、静的解析ツール(mypy、pyright等)によるエラー検出を可能にする。
1.1.1 基本的な型アノテーション:変数、関数引数、戻り値に型を指定できる。
# 変数への型ヒント
name: str = "Python"
age: int = 30
scores: list[int] = [85, 90, 78]
config: dict[str, str] = {"host": "localhost", "port": "8080"}
# 関数への型ヒント
def greet(name: str, times: int = 1) -> str:
return f"Hello, {name}! " * times
# Python 3.10以降: X | Yでユニオン型
def process(value: int | str) -> str:
return str(value)
1.1.2 typingモジュールの主要な型:Table 1に頻出する型を示す。
Table 1. typingモジュールの主要な型
| 型 | 用途 | Python 3.9+での代替 |
|---|---|---|
| List[T] | リスト | list[T] |
| Dict[K, V] | 辞書 | dict[K, V] |
| Optional[T] | T | None | T | None |
| Union[A, B] | AまたはB | A | B |
| Callable[[Args], R] | 呼び出し可能オブジェクト | collections.abc.Callable |
| Any | 任意の型(型チェック無効化) | - |
| TypeVar | ジェネリック型変数 | - |
from typing import Callable, TypeVar
T = TypeVar('T')
# ジェネリック関数
def first(items: list[T]) -> T | None:
return items[0] if items else None
# 高階関数の型付け
def apply_twice(f: Callable[[int], int], x: int) -> int:
return f(f(x))
result = apply_twice(lambda n: n * 2, 5) # 20
1.2 Protocol/Generic
1.2.1 Protocol:構造的部分型(Structural Subtyping)を実現する。クラスが特定のメソッドやプロパティを持っていれば、明示的な継承なしにプロトコルを満たすとみなされる[2]。
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
class Circle:
def draw(self) -> None:
print("Drawing circle")
class Square:
def draw(self) -> None:
print("Drawing square")
def render(shape: Drawable) -> None:
shape.draw()
# Circle, SquareはDrawableを継承していないが、
# draw()メソッドを持つため、Drawableプロトコルを満たす
render(Circle()) # OK
render(Square()) # OK
1.2.2 Generic:型パラメータを持つクラスを定義できる。Python 3.12以降では簡潔な構文が利用可能。
# Python 3.11以前
from typing import Generic, TypeVar
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()
# Python 3.12以降: 簡潔な構文
class Stack[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()
int_stack: Stack[int] = Stack()
int_stack.push(42)
2. dataclassとPydantic
2.1 dataclass:Python 3.7で導入された標準ライブラリの機能で、データを保持するクラスを簡潔に定義できる[3]。
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class User:
name: str
email: str
age: int = 0
created_at: datetime = field(default_factory=datetime.now)
user = User("Alice", "alice@example.com", 30)
print(user) # User(name='Alice', email='alice@example.com', age=30, created_at=...)
# イミュータブルなdataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
p = Point(1.0, 2.0)
# p.x = 3.0 # FrozenInstanceError
2.2 dataclassの主要オプション:Table 2にデコレータのオプションを示す。
Table 2. @dataclassの主要オプション
| オプション | デフォルト | 効果 |
|---|---|---|
| frozen | False | Trueでイミュータブル化 |
| order | False | Trueで比較メソッド生成(<, <=, >, >=) |
| slots | False | True で __slots__ 使用(3.10+) |
| kw_only | False | Trueで全フィールドをキーワード専用に(3.10+) |
2.3 Pydantic:バリデーション機能を備えたデータクラスライブラリ。FastAPIのリクエスト/レスポンスモデルとして広く使用される。
from pydantic import BaseModel, EmailStr, field_validator
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int
@field_validator('age')
@classmethod
def age_must_be_positive(cls, v: int) -> int:
if v < 0:
raise ValueError('age must be positive')
return v
# バリデーション成功
user = UserCreate(name="Alice", email="alice@example.com", age=30)
# バリデーション失敗
try:
invalid = UserCreate(name="Bob", email="invalid-email", age=-5)
except Exception as e:
print(e) # validation error details
2.4 dataclass vs Pydantic:Table 3に両者の比較を示す。
Table 3. dataclassとPydanticの比較
| 観点 | dataclass | Pydantic |
|---|---|---|
| 依存関係 | 標準ライブラリ | 外部パッケージ |
| バリデーション | なし | 強力な組み込み機能 |
| JSON変換 | 手動実装が必要 | model_dump(), model_validate() |
| パフォーマンス | 高速 | バリデーションコストあり |
| 用途 | 内部データ構造 | API境界、設定管理 |
3. 構造的パターンマッチング
Python 3.10で導入されたmatch文は、値のパターンに基づく分岐を可能にする[4]。単純なswitch-case以上の強力なパターンマッチング機能を提供する。
3.1 基本的なパターン:リテラル、変数、ワイルドカードによるマッチング。
def http_status(status: int) -> str:
match status:
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
case _:
return f"Unknown status: {status}"
print(http_status(200)) # OK
print(http_status(999)) # Unknown status: 999
3.2 構造パターン:シーケンス、マッピング、クラスの構造にマッチできる。
# シーケンスパターン
def describe_list(items: list) -> str:
match items:
case []:
return "empty"
case [x]:
return f"single: {x}"
case [x, y]:
return f"pair: {x}, {y}"
case [first, *rest]:
return f"first: {first}, rest: {rest}"
print(describe_list([1, 2, 3, 4])) # first: 1, rest: [2, 3, 4]
# マッピングパターン
def process_event(event: dict) -> str:
match event:
case {"type": "click", "x": x, "y": y}:
return f"Click at ({x}, {y})"
case {"type": "keypress", "key": key}:
return f"Key pressed: {key}"
case {"type": type_}:
return f"Unknown event type: {type_}"
case _:
return "Invalid event"
print(process_event({"type": "click", "x": 100, "y": 200}))
# Click at (100, 200)
3.3 クラスパターン:dataclassやNamedTupleとの組み合わせが強力。
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
@dataclass
class Circle:
center: Point
radius: float
@dataclass
class Rectangle:
top_left: Point
width: float
height: float
def area(shape) -> float:
match shape:
case Circle(center=_, radius=r):
return 3.14159 * r * r
case Rectangle(top_left=_, width=w, height=h):
return w * h
case _:
raise ValueError("Unknown shape")
print(area(Circle(Point(0, 0), 5))) # 78.53975
print(area(Rectangle(Point(0, 0), 10, 20))) # 200
3.4 ガード条件:ifでマッチ条件を追加できる。
def classify_number(n: int) -> str:
match n:
case x if x < 0:
return "negative"
case 0:
return "zero"
case x if x % 2 == 0:
return "positive even"
case _:
return "positive odd"
4. 非同期処理
4.1 async/await
Python 3.5で導入されたasync/await構文は、コルーチンベースの非同期処理を直感的に記述可能にする[5]。I/Oバウンドな処理を効率的に並行実行できる。
import asyncio
async def fetch_data(url: str) -> str:
"""非同期でデータを取得(シミュレーション)"""
print(f"Fetching {url}...")
await asyncio.sleep(1) # I/O待ちをシミュレート
return f"Data from {url}"
async def main():
# 順次実行(3秒かかる)
result1 = await fetch_data("https://api1.example.com")
result2 = await fetch_data("https://api2.example.com")
result3 = await fetch_data("https://api3.example.com")
# 並行実行(1秒で完了)
results = await asyncio.gather(
fetch_data("https://api1.example.com"),
fetch_data("https://api2.example.com"),
fetch_data("https://api3.example.com"),
)
print(results)
asyncio.run(main())
Fig. 1に同期処理と非同期処理の比較を示す。
4.2 asyncio
4.2.1 主要な関数:Table 4にasyncioの主要関数を示す。
Table 4. asyncioの主要関数
| 関数 | 用途 |
|---|---|
| asyncio.run() | エントリーポイントからコルーチンを実行 |
| asyncio.gather() | 複数コルーチンを並行実行し、全結果を取得 |
| asyncio.create_task() | コルーチンをTaskとしてスケジュール |
| asyncio.wait_for() | タイムアウト付きで待機 |
| asyncio.sleep() | 非同期スリープ |
| asyncio.Queue() | 非同期キュー |
4.2.2 タスクのキャンセルとタイムアウト:
import asyncio
async def long_running_task():
try:
await asyncio.sleep(10)
return "completed"
except asyncio.CancelledError:
print("Task was cancelled")
raise
async def main():
# タイムアウト付き実行
try:
result = await asyncio.wait_for(
long_running_task(),
timeout=2.0
)
except asyncio.TimeoutError:
print("Task timed out")
# 手動キャンセル
task = asyncio.create_task(long_running_task())
await asyncio.sleep(1)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("Caught cancellation")
asyncio.run(main())
4.2.3 非同期コンテキストマネージャとイテレータ:
import asyncio
from typing import AsyncIterator
# 非同期コンテキストマネージャ
class AsyncResource:
async def __aenter__(self):
print("Acquiring resource")
await asyncio.sleep(0.1)
return self
async def __aexit__(self, *args):
print("Releasing resource")
await asyncio.sleep(0.1)
# 非同期ジェネレータ
async def async_range(n: int) -> AsyncIterator[int]:
for i in range(n):
await asyncio.sleep(0.1)
yield i
async def main():
async with AsyncResource() as resource:
async for i in async_range(5):
print(i)
asyncio.run(main())
5. Python 3.10-3.13新機能
Table 5に各バージョンの主要な新機能をまとめる。
Table 5. Python 3.10-3.13の主要新機能
| バージョン | 機能 | 概要 |
|---|---|---|
| 3.10 | match文 | 構造的パターンマッチング |
| 3.10 | X | Y構文 | Union型の簡潔な記法 |
| 3.10 | ParamSpec | デコレータの型付け改善 |
| 3.11 | 例外グループ | ExceptionGroup, except* |
| 3.11 | tomllib | TOML パーサー標準搭載 |
| 3.11 | Self型 | メソッドの戻り値型として自クラスを指定 |
| 3.12 | 型パラメータ構文 | class C[T]: / def f[T](): |
| 3.12 | f-string改善 | ネストした引用符、コメント可能 |
| 3.13 | Free-threaded mode | GIL無効化オプション(実験的) |
| 3.13 | JIT compiler | 実験的JITコンパイラ |
5.1 Python 3.11: 例外グループ:複数の例外を同時に扱える。
# 例外グループの発生
def process_items(items):
errors = []
for item in items:
try:
# 処理
if item < 0:
raise ValueError(f"Negative value: {item}")
except ValueError as e:
errors.append(e)
if errors:
raise ExceptionGroup("Processing errors", errors)
# except*で特定の例外を捕捉
try:
process_items([1, -2, 3, -4])
except* ValueError as eg:
print(f"Caught {len(eg.exceptions)} ValueErrors")
except* TypeError as eg:
print(f"Caught TypeErrors")
5.2 Python 3.12: 型パラメータ構文:ジェネリクスの記述が大幅に簡潔化。
# Python 3.11以前
from typing import TypeVar, Generic
T = TypeVar('T')
K = TypeVar('K')
V = TypeVar('V')
class Container(Generic[T]):
def __init__(self, value: T) -> None:
self.value = value
def first[T](items: list[T]) -> T | None: # エラー
# Python 3.12以降
class Container[T]:
def __init__(self, value: T) -> None:
self.value = value
def first[T](items: list[T]) -> T | None:
return items[0] if items else None
# 型制約も簡潔に
def add[T: (int, float)](a: T, b: T) -> T:
return a + b
5.3 Python 3.12: f-string改善:
# Python 3.11以前: ネストした引用符が難しい
# name = f"User: {user['name']}" # 異なる引用符を使う必要あり
# Python 3.12以降: 同じ引用符でネスト可能
name = f"User: {user["name"]}"
# 複数行とコメントも可能
result = f"""
Name: {
user["name"] # ユーザー名
}
Age: {
user["age"] # 年齢
}
"""
References
[1] G. van Rossum, J. Lehtosalo, Ł. Langa, "PEP 484 -- Type Hints," python.org, 2014.
[2] I. Levkivskyi et al., "PEP 544 -- Protocols: Structural subtyping," python.org, 2017.
[3] E. Smith, "PEP 557 -- Data Classes," python.org, 2017.
[4] B. Cannon, G. van Rossum, T. Wouters, "PEP 634 -- Structural Pattern Matching," python.org, 2020.
[5] Y. Selivanov, "PEP 492 -- Coroutines with async and await syntax," python.org, 2015.
本コンテンツは2025年12月時点の情報に基づいて作成されている。Python 3.13の機能は実験的なものを含み、将来のバージョンで変更される可能性がある。最新の情報は公式ドキュメントを参照されたい。