Bạn đã dùng with open(...) để đọc file. Cơ chế đứng sau nó gọi là context manager — và bạn hoàn toàn có thể tự viết một cái.
Vì sao cần with?
# Cách dễ quên đóng file
f = open("data.txt")
noi_dung = f.read()
f.close() # nếu ở giữa có lỗi, dòng này không bao giờ chạy
# Cách an toàn
with open("data.txt") as f:
noi_dung = f.read()
# file tự đóng, kể cả khi có ngoại lệ
Tự viết bằng class
class MoKetNoi:
def __enter__(self):
print("Mở kết nối")
self.conn = "connection"
return self.conn
def __exit__(self, exc_type, exc_value, tb):
print("Đóng kết nối")
return False # False = không nuốt ngoại lệ
with MoKetNoi() as conn:
print("Đang dùng", conn)
Cách gọn hơn với contextlib
from contextlib import contextmanager
import time
@contextmanager
def do_thoi_gian(ten):
bat_dau = time.perf_counter()
try:
yield
finally:
print(f"{ten}: {time.perf_counter() - bat_dau:.3f}s")
with do_thoi_gian("Tải dữ liệu"):
time.sleep(1)
Phần trước yield chạy khi vào khối with, phần trong finally chạy khi ra — kể cả khi có lỗi.
Mở nhiều tài nguyên cùng lúc
with open("vao.txt") as f_vao, open("ra.txt", "w") as f_ra:
for dong in f_vao:
f_ra.write(dong.upper())
Dùng ở đâu trong thực tế
- Kết nối database — tự commit hoặc rollback
- Khóa (lock) trong đa luồng — tự nhả khóa
- Đổi thư mục làm việc tạm thời rồi trả về chỗ cũ
- Đo thời gian, ghi log bao quanh một đoạn xử lý
Quy tắc đơn giản: hễ có cặp “mở — đóng”, “bật — tắt”, “chiếm — nhả”, hãy nghĩ tới context manager.
