Python|with構文でリソース解放漏れを起こさない設計法
この記事のポイント
- with構文のリソース解放責務設計をレビューできる
 - 実務で頻発する解放漏れ・責務崩壊パターンを見抜ける
 - finally・tryとの使い分けを含めた設計視点を身につける
 
そもそもwith構文とは
Pythonのwith構文はコンテキストマネージャを利用し、リソース解放や後処理を自動化する仕組みです。
with open("file.txt", "r") as f:
    data = f.read()__enter__()でリソース確保__exit__()で解放処理
ファイル操作・ロック管理・ネットワーク接続など様々な場面で使われています。
なぜこれをレビューするのか
現場で頻発する失敗は以下の通りです。
- with構文を使わず明示解放を強要
 - 複数リソースの開放順序崩壊
 - コンテキストマネージャ責務の肥大化
 - 例外時に解放漏れが発生
 
レビューアーは 「解放責務が誰にあるのか」 を常に読み解きます。
レビューアー視点
- with構文が適切に利用されているか
 - 複数リソース管理の順序整理
 - コンテキスト化すべき独自リソースが裸で使われていないか
 - finally濫用・冗長tryがないか
 - 開放忘れの可能性が排除されているか
 
開発者視点
- 可能な限りwith化で責務移譲する
 - 例外発生有無に関係なく確実に開放される構造にする
 - 複数リソースはネストではなく分離管理を検討
 - 独自リソースはcontextlibを活用し自前マネージャ化
 - finallyは最低限に限定
 
良い実装例
なぜこの実装が良いのか
- with構文で自動解放保証
 - 複数リソースをネスト順序で安全管理
 - 例外発生有無に関わらず必ずリソース解放
 - finally依存ゼロで責務明確
 
# api_request_archiver.py
import gzip
import shutil
def archive_request_log(source_file, archive_file):
    with open(source_file, "rb") as src, gzip.open(archive_file, "wb") as dst:
        shutil.copyfileobj(src, dst)補足
リソース確保〜解放までの責務が明確にwithブロックで表現されています。開発者がうっかり解放忘れを起こせない安全設計です。
レビュー観点
- with構文活用有無(原則使い得)
 - ネスト順序の妥当性
 - 独自リソースはcontextlib活用しているか
 - 明示解放処理の残存有無
 - finally濫用箇所の排除
 
良くない実装例: ケース1(明示解放依存)
# bad_manual_close.py
import gzip
import shutil
def archive_request_log(source_file, archive_file):
    src = open(source_file, "rb")
    dst = gzip.open(archive_file, "wb")
    try:
        shutil.copyfileobj(src, dst)
    finally:
        src.close()
        dst.close()
@Reviewer明示解放依存は例外網羅性の欠落や保守性低下を招きます。with構文を活用してください。
問題点
- finally依存
 - 解放責務の漏洩
 - 記述負荷上昇
 
改善例
# good_with_context.py
import gzip
import shutil
def archive_request_log(source_file, archive_file):
    with open(source_file, "rb") as src, gzip.open(archive_file, "wb") as dst:
        shutil.copyfileobj(src, dst)リソース解放責務は常に言語機構へ任せる方が安全です。
レビュー時は「自力close」を見つけ次第指摘対象にします。
良くない実装例: ケース2(独自リソースのcontext未対応)
# bad_custom_resource.py
class CustomConnection:
    def connect(self):
        print("接続確立")
    def disconnect(self):
        print("接続終了")
def use_custom_resource():
    conn = CustomConnection()
    conn.connect()
    try:
        # 実処理
        pass
    finally:
        conn.disconnect()
@Reviewer独自リソースもcontextlib活用でコンテキスト化してください。明示close排除が原則です。
問題点
- contextlib未活用
 - finally乱用
 - API利用側で開放責務発生
 
改善例
# good_custom_context_manager.py
from contextlib import contextmanager
class CustomConnection:
    def connect(self):
        print("接続確立")
    def disconnect(self):
        print("接続終了")
@contextmanager
def managed_connection():
    conn = CustomConnection()
    conn.connect()
    try:
        yield conn
    finally:
        conn.disconnect()
def use_custom_resource():
    with managed_connection() as conn:
        # 実処理
        pass独自リソースでも可能な限りcontextlibでコンテキスト化し、利用者側はwithだけを意識するAPI構造が理想形です。
良くない実装例: ケース3(ネスト崩壊・開放順序設計漏れ)
# bad_nested_context.py
import gzip
import shutil
def archive_request_log(source_file, archive_file):
    src = open(source_file, "rb")
    try:
        dst = gzip.open(archive_file, "wb")
        try:
            shutil.copyfileobj(src, dst)
        finally:
            dst.close()
    finally:
        src.close()
@Reviewerネスト構造が手動管理となっており保守性が低下しています。with構文で自然に順序を表現してください。
問題点
- ネストがtry依存で複雑化
 - 開放順序をコード維持者が常に考慮必要
 - 構文上の事故リスク上昇
 
改善例
# good_with_nested.py
import gzip
import shutil
def archive_request_log(source_file, archive_file):
    with open(source_file, "rb") as src, gzip.open(archive_file, "wb") as dst:
        shutil.copyfileobj(src, dst)ネスト構造はwith構文が最も自然に安全順序を提供します。
レビューで手動ネストは即時指摘対象です。
観点チェックリスト
まとめ
リソース解放責務は設計全体の健全性を如実に表す鏡です。
レビューアーはwith構文活用を最優先観点とし、
「明示解放箇所=レビュー優先指摘箇所」として読んでいくと実務で非常に高精度に指摘できます。
責務は持たせない、任せる、機構に移譲する。
with構文レビューは現場設計者育成における極めて重要な教材となります。