首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python with 陳述式完整指南:上下文管理器協議與資源管理

     編輯:更多程式設計知識

with 陳述式簡化 try/finally 資源釋放模式,確保檔案、鎖、資料庫連線等資源在正常或異常結束時都能正確釋放。本文完整說明協議介面、內建支援類型、自訂上下文管理器與巢狀用法。

基本語法與等價轉換


# 傳統寫法
f = open('data.txt')
try:
    data = f.read()
finally:
    f.close()

# with 寫法 (Python 2.5+ / 2.6+ 預設啟用)
with open('data.txt') as f:
    data = f.read()
# 區塊結束自動呼叫 f.close()

上下文管理器協議

支援 with 的物件必須實作兩個特殊方法:


class MyContext:
    def __enter__(self):
        print("進入區塊")
        return "resource_object"  # 回傳值綁定給 as 變數
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("離開區塊")
        # exc_type/val/tb 為異常資訊,正常結束皆為 None
        # 回傳 True 可吞沒異常,False/None 則繼續拋出
        return False

with MyContext() as resource:
    print("使用資源:", resource)
# 輸出:
# 進入區塊
# 使用資源: resource_object
# 離開區塊

異常處理流程


class ErrorContext:
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            print(f"捕獲異常: {exc_type.__name__}: {exc_val}")
            return True  # 吞沒異常,不再傳播
        return False

with ErrorContext():
    raise ValueError("測試錯誤")
print("程式繼續執行")  # 因為 __exit__ 回傳 True,異常被吞沒

內建支援 with 的類型

類型__exit__ 行為
檔案物件 (open())關閉檔案
執行緒鎖 (threading.Lock)釋放鎖
資料庫連線 (sqlite3.connect)提交/回滾交易
decimal.localcontext恢復精度設定
contextlib.closing呼叫 close()

contextlib 輔助工具

1. @contextmanager 裝飾器 (生成器寫法)


from contextlib import contextmanager

@contextmanager
def open_file(path, mode):
    f = open(path, mode)
    try:
        yield f  # yield 前為 __enter__,yield 後為 __exit__
    finally:
        f.close()

with open_file('test.txt', 'w') as f:
    f.write('hello')

2. closing() 通用關閉器


from contextlib import closing
import urllib.request

with closing(urllib.request.urlopen('https://example.com')) as page:
    html = page.read()
# 自動呼叫 page.close()

3. nested() / ExitStack() 多重上下文 (Python 3.3+)


from contextlib import ExitStack

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in file_list]
    # 全部自動關閉,即使中間發生異常
    data = [f.read() for f in files]

巢狀 with 陳述式


# Python 2.7+ / 3.1+ 支援多重上下文
with open('in.txt') as fin, open('out.txt', 'w') as fout:
    for line in fin:
        fout.write(line.upper())

# 等價於
with open('in.txt') as fin:
    with open('out.txt', 'w') as fout:
        for line in fin:
            fout.write(line.upper())

實務應用範例

資料庫交易管理


import sqlite3
from contextlib import contextmanager

@contextmanager
def transaction(conn):
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise

with sqlite3.connect('db.sqlite') as conn:
    with transaction(conn):
        conn.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
        conn.execute("INSERT INTO users (name) VALUES (?)", ("Bob",))
    # 自動 commit 或 rollback

計時器上下文


import time
from contextlib import contextmanager

@contextmanager
def timer(label):
    start = time.time()
    try:
        yield
    finally:
        print(f"{label}: {time.time() - start:.3f}s")

with timer("資料處理"):
    process_large_data()

最佳實務

  1. 所有資源獲取釋放配對操作 (檔案、鎖、連線、臨時目錄) 必須使用 with。
  2. 自訂類別實作 __enter__/__exit__ 或使用 @contextmanager。
  3. __exit__ 回傳 True 僅用於刻意吞沒特定異常,預設回傳 False。
  4. 避免在 __enter__ 中執行可能失敗的複雜邏輯,若失敗 __exit__ 不會被呼叫。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved