Python | ファイル操作など:with 文

Python
スポンサーリンク

概要(with 文は「後片付けを自動化して安全にする」ための構文)

with 文は、ファイルやネットワーク接続など「使ったら必ず片付けが必要」な資源を、安全・簡潔に扱うための構文です。ブロックを抜けるタイミング(正常終了でも例外でも)で自動的に後処理が走るため、close の書き忘れや例外時の漏れを防げます。ファイル操作では「with open(…) as f:」が定番で、これだけで開閉の管理が完了します。

# 読み込み:ブロックを抜けると f は自動で閉じられる
with open("notes.txt", "r", encoding="utf-8") as f:
    text = f.read()

# 書き込み:例外が起きても自動でクローズされる
with open("out.txt", "w", encoding="utf-8") as f:
    f.write("こんにちは\n")
Python

基本の仕組み(ここが重要)

with は「コンテキストマネージャ」を使って後始末を自動化する

with は、対象オブジェクトが持つ enterexit というメソッドを呼び出す仕組みです。ブロックに入る前に enter が実行され、ブロックを抜けるときに exit が必ず呼ばれて後片付け(クローズ、解放、ロールバックなど)を行います。open が返すファイルオブジェクトはこの仕組みを備えているため、with と相性が抜群です。

# 実装イメージ(理解用)
class MyCtx:
    def __enter__(self):
        print("start")   # ここで準備
        return self
    def __exit__(self, exc_type, exc, tb):
        print("end")     # ここで後始末(例外があれば exc を受け取れる)

with MyCtx() as c:
    pass  # ブロックを抜けると必ず end が呼ばれる
Python

try/finally より短く確実(例外でも後処理が走る)

従来の「open → try → finally で close」は正しいものの、毎回書くのは面倒で漏れやすい。with は同じことを一行で表現でき、例外発生時も確実に後処理されます。初心者は「資源を掴んだら with」が基本ルールだと覚えてください。

# 非推奨(忘れやすい):
f = open("data.txt", "r", encoding="utf-8")
try:
    text = f.read()
finally:
    f.close()

# 推奨(短くて確実):
with open("data.txt", "r", encoding="utf-8") as f:
    text = f.read()
Python

ファイル操作での使い方(読み込み・書き込み・追記)

読み込みは “r”、書き込みは “w”、追記は “a”、新規のみは “x”

モードの選択が動作を決めます。上書きしたくない成果物の生成には “x” が安全です。テキストは原則 UTF-8 を指定し、表示や比較前には改行の扱い(strip や newline=”\n”)を意識しましょう。

# 読み込み(UTF-8)
with open("input.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.strip())

# 追記(末尾に追加)
with open("log.txt", "a", encoding="utf-8", newline="\n") as f:
    f.write("started\n")

# 新規のみ(既存なら例外)
with open("report.txt", "x", encoding="utf-8") as f:
    f.write("new report\n")
Python

バイナリは “rb”/”wb”(bytes を扱う)

画像・PDF・ZIP などはバイナリモードで開き、bytes を読み書きします。テキストモードで扱うと壊れるので注意してください。

# 画像コピー(バイナリ)
with open("in.png", "rb") as src, open("out.png", "wb") as dst:
    while True:
        chunk = src.read(1 << 20)  # 1MB
        if not chunk:
            break
        dst.write(chunk)
Python

実務での応用(複数資源・標準ライブラリのコンテキスト)

複数の資源を同時に扱う(カンマで並べる)

ファイルを二つ以上開く、ロックや接続とファイルを同時に掴む、といった場面では with を並べられます。どれかで例外が起きても、掴んだ分は順に確実に後処理されます。

# 2つのファイルを同時に扱う
with open("src.txt", "r", encoding="utf-8") as src, \
     open("dst.txt", "w", encoding="utf-8", newline="\n") as dst:
    for line in src:
        dst.write(line.upper())
Python

contextlib を使う便利ワザ(抑止・差し替え・自作)

contextlib は with の相棒です。よく使うのは suppress(特定例外の無視)と redirect_stdout(print の出力先差し替え)、そして自作のコンテキストを簡単に作る contextmanager。

import contextlib
from pathlib import Path

# 特定の例外を抑止(ファイルが無ければスキップ)
with contextlib.suppress(FileNotFoundError):
    print(Path("optional.txt").read_text(encoding="utf-8"))

# print の出力先をファイルへ差し替え
with open("app.log", "a", encoding="utf-8") as f, contextlib.redirect_stdout(f):
    print("INFO started")  # ログへ書かれる

# 簡易的な自作コンテキスト(例:作業ディレクトリを一時変更)
import os
@contextlib.contextmanager
def chdir(path: str):
    old = os.getcwd()
    os.chdir(path)
    try:
        yield
    finally:
        os.chdir(old)

with chdir("data"):
    print(os.getcwd())  # data に入っている間だけ
Python

重要ポイントの深掘り(設計と安全性)

「先に with を置く」習慣でバグを防ぐ

資源を掴むコードは最初に with を置き、ブロック内に処理を閉じ込めると、スコープが明確になり後始末漏れがゼロになります。関数の途中で open をして後で close…という書き方は避けましょう。

例外時の挙動とコミット/ロールバックの設計

exit は例外情報を受け取れます。データベース接続やトランザクションでは、ブロックが例外で抜けたらロールバック、正常終了ならコミットという設計が自然です。外部資源は「失敗時の安全側」をデフォルトに。

原子的な書き込み(途中で落ちても壊さない)

重要ファイルは一時ファイルへ書いてから置き換えると安全です。with で書く→ flush → fsync → replace の順にすると、障害時でも中途半端な内容が表に出ません。

import os
from pathlib import Path

def atomic_write(path: str, data: str) -> None:
    p = Path(path)
    tmp = p.with_suffix(p.suffix + ".tmp")
    with tmp.open("w", encoding="utf-8", newline="\n") as f:
        f.write(data)
        f.flush()
        os.fsync(f.fileno())  # OSバッファも同期
    os.replace(tmp, p)
Python

例題で身につける(定番から一歩先まで)

例題1:設定ファイルを安全に読み込む

def load_kv(path: str) -> dict[str, str]:
    kv = {}
    with open(path, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith("#"):  # 空行・コメント
                continue
            k, v = line.split("=", 1)
            kv[k.strip()] = v.strip()
    return kv
Python

例題2:ログ追記(print と with の組み合わせ)

def log(msg: str) -> None:
    with open("app.log", "a", encoding="utf-8") as f:
        print("INFO", msg, file=f)

log("started")
log("done")
Python

例題3:巨大ファイルのストリーミングコピー

def stream_copy(src: str, dst: str, buf: int = 1 << 20) -> None:
    with open(src, "rb") as s, open(dst, "wb") as d:
        while True:
            chunk = s.read(buf)
            if not chunk:
                break
            d.write(chunk)
Python

例題4:標準出力をファイルに一時的に切り替える

import contextlib

with open("stdout.txt", "w", encoding="utf-8") as f, contextlib.redirect_stdout(f):
    print("この行はファイルへ出力されます")
Python

まとめ

with 文は「資源を掴んだら必ず片付ける」をプログラムに組み込むための最短ルートです。open と組み合わせればファイルの開閉が自動化され、例外でも漏れません。複数資源の同時扱い、contextlib による応用(suppress/redirect/contextmanager)、原子的な書き込みなど、現場で効くテクニックとも相性が良い。設計の基本線は「資源の寿命を with ブロックに閉じ込める」。この習慣が身につけば、コードが短く、堅牢で、安心して運用できるようになります。

タイトルとURLをコピーしました