โมดูล contextlib ของไลบรารีมาตรฐาน Python กำหนดคลาส ContextManager ที่อ็อบเจ็กต์ของมันจัดการทรัพยากรภายในโปรแกรมอย่างเหมาะสม Python มีคีย์เวิร์ด with ที่ทำงานกับ context manager อ็อบเจ็กต์ไฟล์ (ซึ่งคืนค่าโดยฟังก์ชันสำเร็จรูป open()) รองรับ ContextManager API ดังนั้นเราจึงมักพบคีย์เวิร์ด with ขณะทำงานกับไฟล์
บล็อกโค้ดต่อไปนี้เปิดไฟล์และเขียนข้อมูลบางส่วนลงไป หลังจากดำเนินการเสร็จสิ้น ไฟล์จะถูกปิด หากไม่ปิด ตัวระบุไฟล์ (file descriptor) อาจรั่ว ทำให้ไฟล์เสียหาย:
f = open("file.txt", "w")
f.write("hello world")
f.close()
อย่างไรก็ตาม การดำเนินการไฟล์เดียวกันนี้ทำได้โดยใช้ความสามารถ context manager ของไฟล์ด้วยไวยากรณ์ดังนี้:
with open("file.txt", "w") as f:
f.write("hello world")
print("file is closed")
ตามที่กล่าวไว้ข้างต้น อ็อบเจ็กต์ไฟล์ implements ContextManager ถูกเปิดใช้งานโดยคีย์เวิร์ด with บล็อก with มีคำสั่งที่จะประมวลผลสำหรับอ็อบเจ็กต์ไฟล์ ทันทีที่บล็อก with จบ อ็อบเจ็กต์ไฟล์จะถูกปิดอัตโนมัติ (เมธอด close() ไม่ต้องเรียกอย่างชัดเจน) อ็อบเจ็กต์ใดๆ ที่ถูกนำเข้าไปในบล็อก with จะทำงานเฉพาะภายในบล็อกและจะถูกกำจัดทันทีที่สิ้นสุด
คลาส ContextManager มีเมธอดสำคัญ 2 ตัวคือ __enter__() และ __exit__():
__enter__() - จะถูกเรียกเมื่อบล็อก with เริ่มต้น แสดงว่าโปรแกรมได้เข้าสู่บริบทรันไทม์ที่เกี่ยวข้องกับอ็อบเจ็กต์นี้
__exit__() - ถูกเรียกเมื่อบล็อก with จบ แสดงว่าโปรแกรมออกจากบริบทรันไทม์ที่เกี่ยวข้องกับอ็อบเจ็กต์นี้
อ็อบเจ็กต์ไฟล์ก็มีเมธอดทั้งสองนี้เหมือนกัน ตามที่ยืนยันได้จากเซสชันตัวแปลต่อไปนี้:
>>> f = open("file.txt", "w")
>>> f.__enter__()
<_io.TextIOWrapper name='file.txt' mode='w' encoding='cp1252'>
>>> f.write("hello world")
11
>>> f.__exit__()
>>> f.write("hello world")
Traceback (most recent call last):
File "", line 1, in
f.write("hello world")
ValueError: I/O operation on closed file.
เมื่อเมธอด __exit__() ถูกเรียก ไฟล์จะถูกปิด นั่นคือเหตุผลที่ ValueError ปรากฏเมื่อเราพยายามเขียนข้อมูลลงไฟล์หลังจากที่มันถูกปิด
ตัวอย่างด้านล่างเป็นการใช้ contextManager แบบทั่วไปมากขึ้น ก่อนอื่นเรากำหนดคลาสที่มีเมธอด __enter__() และ __exit__() และเปิดใช้งาน contextManager สำหรับอ็อบเจ็กต์ของมันโดยใช้คำสั่ง with:
import contextlib
class WithExample:
def __init__(self):
print("object initialized")
def __enter__(self):
print("entered context")
def __exit__(self, *args):
print("exited context")
with WithExample() as w:
print('this is a contextlib example')
print('used by with statement')
print('end of with block')
เอาต์พุทแสดงว่าทันทีที่บล็อก with เริ่มต้น เมธอด __enter__() จะถูกรัน คำสั่งภายในบล็อกได้รับการประมวลผล เมื่อบล็อกจบ เมธอด __exit__() ก็ถูกเรียกอัตโนมัติ:
object initialized entered context this is a contextlib example used by with statement exited context end of with block
โมดูล contextlib มีตัวแต่ง @contextmanager ซึ่งช่วยให้เราเขียนฟังก์ชันโรงงาน (factory function) แบบเจเนอเรเตอร์เพื่อรองรับคำสั่ง with อัตโนมัติ Context management ของอ็อบเจ็กต์ไฟล์ด้วยตัวแต่งเป็นดังนี้:
from contextlib import contextmanager
@contextmanager
def openfile(name):
try:
f = open(name, 'w')
yield f
finally:
f.close()
with openfile('file.txt') as f:
f.write('hello world')
print('file is closed')
ContextManager จึงเป็นคุณสมบัติที่มีประโยชน์มากของ Python สำหรับการจัดการทรัพยากรในโปรแกรมอย่างมีประสิทธิภาพ