Bỏ qua tới nội dung chính
Quay lại tin tức

Context Manager trong Python vượt ra ngoài phạm vi mở tệp

Machine Learning Mastery· Nahla Davies· 27/10/2026general

Đồng hồ bấm giờ trong kịch bản mở đầu là trường hợp nhỏ nhất của mô hình này.

Trình quản lý ngữ cảnh (Context Manager) trong Python: Không chỉ dừng lại ở thao tác mở tệp Tác giả: Nahla Davies, ngày 27/10/2026, chuyên mục Practical Python 0 Chia sẻ Đăng bài Chia sẻ Bài viết này phân tích cơ chế vận hành chuyên sâu của các trình quản lý ngữ cảnh (context manager) trong Python và phương pháp ứng dụng vào xử lý triệt để các bài toán giải phóng tài nguyên, vượt ra ngoài phạm vi thao tác với tệp tin thông thường. Các nội dung trọng tâm bao gồm: Giao thức hoạt động của câu lệnh with, vai trò thực tế của phương thức __enter__() và __exit__(), cùng ý nghĩa của giá trị trả về từ __exit__(). Phương pháp xây dựng context manager tùy chỉnh thông qua việc sử dụng lớp (class), bộ trang trí (decorator) @contextmanager và các công cụ thuộc thư viện chuẩn như TemporaryDirectory, patch.dict(). Cách thức quản lý an toàn các tài nguyên có số lượng biến đổi trong thời gian chạy (runtime) bằng ExitStack, cùng thời điểm cần thiết kế một lớp bao đóng chuyên biệt. Kịch bản đánh giá (evaluation script) thực hiện nhiều thao tác dọn dẹp sơ bộ trước khi tiến hành xử lý dữ liệu: tạo một thư mục tạm cho tập dữ liệu kiểm thử được giải nén, thiết lập biến môi trường để trỏ bộ nhớ đệm (cache) của mô hình sang ổ đĩa tốc độ cao, hạ mức ghi nhật ký (log) của thư viện xuống mức WARNING nhằm tránh làm quá tải giao diện điều khiển (console), và kích hoạt đồng hồ bấm giờ. Phần cuối của tệp mã nguồn sẽ hoàn tác toàn bộ các thiết lập trên theo đúng trình tự: xóa thư mục tạm, khôi phục biến môi trường, đặt lại mức ghi nhật ký và hiển thị thời gian thực thi. Quy trình dọn dẹp này chỉ vận hành trọn vẹn khi kịch bản chạy thành công. Khi gặp bản ghi lỗi khiến hàm evaluate() phát sinh ngoại lệ giữa chừng, toàn bộ các thao tác dọn dẹp phía sau sẽ bị bỏ qua. Người sử dụng hệ thống tiếp theo sẽ gặp phải đường dẫn bộ nhớ đệm không còn hợp lệ, hệ thống nhật ký bị tắt bất thường và 3 GB tệp tin tạm không được giải phóng. scratch = Path("scratch"); scratch.mkdir(exist_ok=True) os.environ["MODEL_CACHE"] = "/fast/cache" logging.getLogger("transformers").setLevel(logging.WARNING) start = time.perf_counter() scores = evaluate(model, extract(archive, scratch)) # phát sinh ngoại lệ khi gặp bản ghi lỗi print(f"took {time.perf_counter() - start:.1f}s") # phần này không được thực thi shutil.rmtree(scratch) del os.environ["MODEL_CACHE"] logging.getLogger("transformers").setLevel(logging.INFO) 1 2 3 4 5 6 7 8 9 10 11 12 scratch = Path("scratch"); scratch.mkdir(exist_ok=True) os.environ["MODEL_CACHE"] = "/fast/cache" logging.getLogger("transformers").setLevel(logging.WARNING) start = time.perf_counter() scores = evaluate(model, extract(archive, scratch)) # phát sinh ngoại lệ khi gặp bản ghi lỗi print(f"took {time.perf_counter() - start:.1f}s") # phần này không được thực thi shutil.rmtree(scratch) del os.environ["MODEL_CACHE"] logging.getLogger("transformers").setLevel(logging.INFO) Phần lớn các lập trình viên Python tiếp cận context manager qua cú pháp with open(path) as f, ghi nhận khả năng tự động đóng tệp và dừng lại ở đó. Tuy nhiên, xử lý tệp tin là ứng dụng cơ bản nhất của context manager. Bản chất của câu lệnh with là thiết lập một khối lệnh bảo đảm việc thoát an toàn: khâu chuẩn bị diễn ra ở phần đầu, khâu dọn dẹp diễn ra ở phần cuối, và bước dọn dẹp luôn được thực thi bất kể khối lệnh chính hoàn thành hay phát sinh ngoại lệ. Khi yêu cầu dọn dẹp mang tính bắt buộc ngay cả lúc chương trình gặp sự cố, việc đặt các thao tác này ngoài khối lệnh là điều bất hợp lý. Cả bốn tác vụ trong kịch bản đánh giá nêu trên đều mang tính ràng buộc tạm thời và đều có thể triển khai thành context manager: một bộ định thời tự động dừng, một thư mục tự động xóa, một trạng thái tự khôi phục và một ngăn xếp tài nguyên tự giải phóng theo đúng trình tự. Đây là lộ trình tiếp cận xuyên suốt: từ giao thức nền tảng của with, các công cụ hỗ trợ trong thư viện chuẩn, đến nguyên tắc lựa chọn giải pháp tối ưu. Câu lệnh with là một ranh giới kiểm soát, không đơn thuần là thủ thuật xử lý tệp tin Tài liệu tham chiếu ngôn ngữ mô tả câu lệnh with như một chuỗi thao tác tuần tự, và chuỗi này có ý nghĩa quan trọng cần ghi nhớ. Mọi mẫu thiết kế dưới đây đều là các biến thể của chuỗi thao tác đó. Python sẽ đánh giá biểu thức đứng sau with để nhận một context manager và gọi phương thức __enter__() của đối tượng này. Bất kể giá trị nào do __enter__() trả về cũng sẽ được gán cho định danh đứng sau as; sau đó khối lệnh được thực thi và phương thức __exit__() được kích hoạt. Bước cuối cùng chính là mấu chốt của toàn bộ quy trình. Phương thức này luôn được gọi khi kết thúc bình thường, khi có lệnh return từ bên trong khối, hoặc khi phát sinh ngoại lệ; do đó, không một tiến trình nào qua khối lệnh có thể bỏ qua quy trình dọn dẹp đã được thiết lập trong __exit__(). Có hai chi tiết thường gây nhầm lẫn, và cả hai đều liên quan đến các giá trị trả về. Thứ nhất, biến đứng sau as sẽ tiếp nhận bất kỳ giá trị nào mà __enter__() trả về; giá trị này thường là chính context manager nhưng không bắt buộc. Hàm open() trả về đối tượng tệp tin, trong khi TemporaryDirectory() trả về một chuỗi đường dẫn. Thứ hai, __exit__() tiếp nhận ba đối số mô tả ngoại lệ, hoặc ba giá trị None khi kết thúc bình thường, và giá trị trả về của chính phương thức này sẽ quyết định tiến trình tiếp theo. Mô hình dữ liệu quy định rất chuẩn xác về điểm này: nếu trả về một giá trị mang tính chân lý (true value), ngoại lệ sẽ bị triệt tiêu; nếu trả về False, hoặc kết thúc hàm mà không có lệnh trả về (tương đương trả về None), ngoại lệ sẽ tiếp tục lan truyền sau bước dọn dẹp. class Announce: def __enter__(self): print("enter") return "the value bound after as" def __exit__(self, exc_type, exc_value, traceback): print("exit", exc_type) # None on a clean exit return False # let any exception propagate with Announce() as x: print(x) 1 2 3 4 5 6 7 8 9 10 11 class Announce: def __enter__(self): print("enter") return "the value bound after as" def __exit__(self, exc_type, exc_value, traceback): print("exit", exc_type) # None on a clean exit return False # let any exception propagate with Announce() as x: print(x) Quy tắc triệt tiêu ngoại lệ là điều cần nắm vững đầu tiên. Sai sót tại điểm này thường diễn ra trong âm thầm. Một phương thức __exit__() kết thúc bằng return True sẽ triệt tiêu toàn bộ ngoại lệ phát sinh bên trong khối lệnh; điều này cũng xảy ra tương tự nếu phương thức trả về kết quả của một lệnh gọi dọn dẹp vốn có giá trị tương đương đúng (truthy). Tập lệnh vẫn tiếp tục vận hành, các kết quả điểm số bị thiếu hụt, và thông tin truy vết ngăn xếp (traceback) vốn có thể giải thích nguyên nhân lại bị triệt tiêu ngay tại ranh giới khối lệnh. Các context manager tùy chỉnh dưới đây tuyệt đối không trả về giá trị mang tính khẳng định (truthy) từ __exit__(). Mọi sự cố phát sinh bên trong khối lệnh đều được giữ nguyên để đối tượng gọi xử lý, đúng theo nguyên tắc kỹ thuật. Sự bảo đảm được thể hiện khái quát qua sơ đồ: cả hai tiến trình thoát khỏi khối lệnh đều phải đi qua __exit__(), và trên tiến trình xử lý ngoại lệ, giá trị trả về của phương thức này là yếu tố duy nhất quyết định việc lỗi có đến được với đối tượng gọi hay không. Nguồn: sơ đồ gốc dựa trên

Nguồn tin: Machine Learning Mastery — Tác giả: Nahla Davies. Bản dịch tiếng Việt do AI thực hiện, có thể có sai sót.