Python の言語仕様の要点
内容の確認日: 2026-10-04
この分野は『Python実践レシピ』の第 3 章 (Python の言語仕様) に対応し、40 問中 7 問と、実践試験でもっとも出題の多い章です。基礎試験で学んだ文法を一歩進め、例外の連鎖・コンテキストマネージャー・ジェネレーター・デコレーターなど、ライブラリを書くときに使う仕組みが問われます。
コードを読んで「何が出力されるか」を答える問題が中心です。どの順番で実行されるか、いつ評価されるかを、実際に動かして確かめながら覚えるのが近道です。
例外処理と例外の連鎖
try 文は、try 節 → (例外があれば) 合う except 節 → (例外がなければ) else 節 → finally 節の順に実行されます。except は上から順に調べ、最初に合った 1 つだけが実行されるので、具体的な例外を先に書きます。
例外を捕捉して別の例外に置き換えるときは、raise 新しい例外 from 元の例外 と書きます。元の例外は新しい例外の __cause__ 属性に入り、トレースバックにも両方が表示されるので、原因をたどれます。
class ConfigError(Exception): passdef load_port(text): try: return int(text) except ValueError as e: raise ConfigError(f"ポート番号が不正: {text}") from etry: load_port("80a")except ConfigError as e: print(e) print(type(e.__cause__).__name__)python3 main.py ポート番号が不正: 80a ValueError
Python 3.11 からは、複数の例外をまとめた ExceptionGroup と、その中から型ごとに取り出して処理する except* 節も使えます。except* は、グループのうち合う例外をすべて処理し、残りを次の except* 節に回します。
try: raise ExceptionGroup("検証エラー", [ValueError("年齢"), TypeError("名前"), ValueError("住所")])except* ValueError as group: print("値の誤り:", [str(e) for e in group.exceptions])except* TypeError as group: print("型の誤り:", [str(e) for e in group.exceptions])python3 main.py 値の誤り: ['年齢', '住所'] 型の誤り: ['名前']
with 文とコンテキストマネージャー
with 文は、ブロックに入るときに __enter__、抜けるときに __exit__ を呼びます。ブロックの中で例外が起きても __exit__ は必ず呼ばれるので、ファイルを閉じる・ロックを解放するなどの後始末を確実に行えます。__enter__ の戻り値は as の変数に入ります。
__exit__ は例外の情報 (型・値・トレースバック) を受け取り、真の値を返すと例外を握りつぶします。普通は None (偽) を返し、例外をそのまま外に伝えます。
簡単なコンテキストマネージャーは、contextlib.contextmanager を付けたジェネレーター関数で書けます。yield の前が入るときの処理、後が抜けるときの処理で、yield した値が as の変数に入ります。例外が起きても後始末をするには、yield を try・finally で囲みます。
from contextlib import contextmanager, suppress@contextmanagerdef section(name): print(f"[{name} 開始]") try: yield name.upper() finally: print(f"[{name} 終了]")with section("load") as label: print("処理中:", label)with suppress(KeyError): {}["missing"]print("KeyError は無視された")python3 main.py [load 開始] 処理中: LOAD [load 終了] KeyError は無視された
contextlib.suppress(例外) は、指定した例外が起きたらそれを無視してブロックを抜けるコンテキストマネージャーです。
構造的パターンマッチ (match 文)
Python 3.10 で追加された match 文は、値の「形」で分岐します。case は上から順に調べ、最初に一致したものだけを実行します。主なパターンは次のとおりです。
・リテラルパターン: case 404: のように値と比べる。case 200 | 201: のように | で「いずれか」を書ける
・取り出しパターン: case code: のように変数名だけを書くと、何にでも一致し、値がその変数に入る
・ワイルドカード: case _: は何にでも一致し、値を取り出さない (最後の「それ以外」に使う)
・シーケンスパターン: case ["go", direction]: のように要素の数と並びで照合する。*rest で残りをまとめて受け取れる
・マッピングパターン: case {"type": "user"}: は、そのキーと値を含む辞書に一致する。余分なキーがあっても一致する
・クラスパターン: case Point(x=0, y=y): は型と属性を照合する。case str(name): は str のときだけ一致する
case の後に if 条件 (ガード) を付けると、パターンが一致し、かつ条件が真のときだけその case が選ばれます。
def handle(event): match event: case {"type": "click", "pos": (x, y)} if x < 0 or y < 0: return "画面外のクリック" case {"type": "click", "pos": (x, y)}: return f"クリック ({x}, {y})" case {"type": "key", "key": "q" | "Q"}: return "終了" case {"type": str(kind)}: return f"未対応: {kind}" case _: return "不正なイベント"print(handle({"type": "click", "pos": (3, 4), "time": 10}))print(handle({"type": "click", "pos": (-1, 4)}))print(handle({"type": "key", "key": "Q"}))print(handle({"type": "scroll"}))print(handle(["click"]))python3 main.py クリック (3, 4) 画面外のクリック 終了 未対応: scroll 不正なイベント
1 つ目のイベントには "time" という余分なキーがありますが、マッピングパターンは余分なキーを無視するので一致します。シーケンスパターンは文字列には一致しない (文字列を 1 文字ずつの並びとはみなさない) 点も覚えておきましょう。
関数の引数とアンパック
引数リストの / より前は位置専用引数 (キーワードで渡せない)、* より後はキーワード専用引数 (位置では渡せない) です。*args は余った位置引数をタプルに、**kwargs は余ったキーワード引数を辞書にまとめます。*args の後に書いた引数もキーワード専用になります。
def request(url, /, method="GET", *, timeout=10, **headers): return f"{method} {url} timeout={timeout} {headers}"print(request("/a"))print(request("/b", "POST", timeout=3, accept="json"))try: request(url="/c")except TypeError: print("url はキーワードで渡せない")python3 main.py GET /a timeout=10 {} POST /b timeout=3 {'accept': 'json'} url はキーワードで渡せない
デフォルト値は関数を定義したときに 1 回だけ評価されます。リストなど変更可能なオブジェクトをデフォルト値にすると、呼び出しをまたいで同じオブジェクトが使われてしまうので、None を既定にして関数の中で作ります。
アンパックは、代入では first, *rest = 値 のように * で残りをまとめて受け取り (常にリストになる)、呼び出しでは f(*リスト, **辞書) で要素を引数に展開します。辞書は {**a, **b} や a | b で合わせられ、同じキーは後ろの値になります。
head, *body, tail = "python"print(head, body, tail)point = {"x": 1, "y": 2}print({**point, "y": 20, "z": 3})print(max(*[3, 9, 4]))python3 main.py p ['y', 't', 'h', 'o'] n {'x': 1, 'y': 20, 'z': 3} 9
内包表記とジェネレーター
内包表記は、[式 for 変数 in 反復可能 if 条件] (リスト)・{キー: 値 for ...} (辞書)・{式 for ...} (集合) の 3 種類です。for を複数書くと、左の for が外側のループになります。
丸かっこで書く (式 for ...) はタプルではなくジェネレーター式で、値を 1 つずつ必要になったときに計算します (遅延評価)。全体をメモリに持たないので、大きなデータの合計などに向いています。
関数の中で yield を使うとジェネレーター関数になり、呼び出すとジェネレーターが返ります。next() や for で値を要求するたびに、前回の yield の続きから実行が再開します。最後まで進んだジェネレーターは空になり、もう一度は使えません。
def read_lines(): for line in ["a", "", "b", "c"]: print(f"(読み込み {line!r})") yield linenon_empty = (line for line in read_lines() if line)print("まだ読み込んでいない")print(next(non_empty))print(list(non_empty))print(list(non_empty))python3 main.py まだ読み込んでいない (読み込み 'a') a (読み込み '') (読み込み 'b') (読み込み 'c') ['b', 'c'] []
yield from サブジェネレーター は、サブジェネレーターの値をそのまま順に返します。サブジェネレーターが return した値は、yield from 式の値になります。
内包表記の中で作ったラムダ式は、変数の値を呼び出したときに参照します (遅延束縛)。[lambda: i for i in range(3)] の 3 つの関数はどれも 2 を返します。作った時点の値を使うには lambda i=i: i のようにデフォルト引数に入れます。
デコレーター
デコレーターは、関数を受け取って別の関数を返す関数です。@deco を付けた関数定義は、定義の直後に func = deco(func) を 1 回だけ実行するのと同じ意味です。重ねて付けると、関数に近い (下の) ものから順に適用されます。
デコレーターが返す内側の関数 (wrapper) には、functools.wraps(func) を付けます。付けないと、関数名 (__name__) や docstring (__doc__) が wrapper のものに置き換わってしまいます。
引数を取るデコレーター (@retry(3) など) は、「デコレーターを返す関数」として、関数を 3 重に入れ子にして書きます。
import functoolsdef retry(times): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): for n in range(1, times + 1): try: return func(*args, **kwargs) except ConnectionError: print(f"{n} 回目の失敗") return "あきらめた" return wrapper return decoratorcalls = 0@retry(3)def fetch(): """データを取得する""" global calls calls += 1 if calls < 3: raise ConnectionError return "成功"print(fetch(), fetch.__name__, fetch.__doc__)python3 main.py 1 回目の失敗 2 回目の失敗 成功 fetch データを取得する
標準ライブラリにも、デコレーターとして使うものがあります。functools.cache・functools.lru_cache (戻り値のキャッシュ)、property・classmethod・staticmethod (メソッドの種類)、dataclasses.dataclass (クラスに付ける) などです。
試験でよくある引っかけ
exceptの順番except Exception:を先に書くと、後ろのexcept ValueError:には届きません。具体的な例外を先に書きます。finallyとreturntry節でreturnしても、関数を抜ける前にfinally節が実行されます。__exit__の戻り値__exit__が真の値を返すと、ブロック内の例外は外に伝わらず、with文の次から処理が続きます。マッピングパターンと余分なキー
case {"type": "admin"}:は、ほかのキーを含む辞書にも一致します。case 変数名:は何にでも一致する変数名だけのパターンは取り出しパターンで、値と比較するわけではありません。定数と比べたいときは
Color.REDのようにドット付きの名前を使います。位置専用引数をキーワードで渡す
/より前の引数をキーワードで渡すとTypeErrorです。変更可能なデフォルト値
def f(items=[])のリストは呼び出しをまたいで共有されます。ジェネレーターは 1 回しか使えない
最後まで進んだジェネレーターをもう一度
list()にすると空になります。inで調べた場合も、見つかるまでの要素は消費されます。重ねたデコレーターの順番
@boldの下に@italicを書くと、bold(italic(func))の順に適用されます。
問題で確認する
この分野の問題を、解説つきで読めます (登録不要)。
出典・参考
- Python エンジニア育成推進協会「Python 実践試験」(出題範囲)
- Python ドキュメント「エラーと例外」
- Python ドキュメント「複合文 (match 文)」
- Python ドキュメント「contextlib」
- Python ドキュメント「functools」
コード例は、実際に実行して結果を確認しています。