概要(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 は、対象オブジェクトが持つ enter と exit というメソッドを呼び出す仕組みです。ブロックに入る前に 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 が呼ばれる
Pythontry/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())
Pythoncontextlib を使う便利ワザ(抑止・差し替え・自作)
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 ブロックに閉じ込める」。この習慣が身につけば、コードが短く、堅牢で、安心して運用できるようになります。
