2.24. Context managers

คำสั่ง with รันโค้ดการตั้งค่า จากนั้นเนื้อหา แล้วโค้ดการล้างข้อมูล -- พร้อมรับประกันว่าการล้างข้อมูลจะทำงานแม้เนื้อหาล้มเหลวระหว่างทาง คู่ของเมธอดที่ให้การตั้งค่าและการล้างข้อมูลเรียกว่า context manager

รูปแบบคือ:

with <expression> as <name>:
    <body>

นิพจน์จะคืนค่า context manager Python เรียกเมธอด __enter__ ของมัน โดยอาจผูกผลลัพธ์กับ <name> รันเนื้อหา แล้วจึงเรียก __exit__ ไม่ว่าเนื้อหาจะทำงานสำเร็จหรือเกิดข้อยกเว้น

Flow diagram: __enter__ runs first, then the body; the body either completes normally or raises; either way, __exit__ runs at the end.

__exit__ ทำงานเมื่อสิ้นสุดบล็อกไม่ว่าจะเกิดอะไรขึ้นภายใน

2.24.1. การใช้งาน context manager

ตัวอย่างทั่วไปคือการเปิดไฟล์:

with open("data.txt") as f:
    text = f.read()

# f is now closed, even if read() failed

open() คืนค่าออบเจ็กต์ไฟล์ที่ตัวมันเองเป็น context manager __enter__ คืนค่าไฟล์ และ __exit__ ปิดมัน บล็อก with ทำให้ "ปิดไฟล์เสมอเมื่อเสร็จสิ้น" เป็นค่าเริ่มต้นแทนที่จะเป็นสิ่งที่ผู้เรียกต้องจำเอง

2.24.1.1. Context manager หลายตัว

คำสั่ง with เดียวสามารถเข้า context manager หลายตัวพร้อมกันได้โดยใช้เครื่องหมายจุลภาคคั่น:

with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read())

เทียบเท่ากับบล็อก with ซ้อนกันสองชั้น แต่แบนกว่า Manager จะถูกเข้าจากซ้ายไปขวาและออกในลำดับย้อนกลับ ถ้า __enter__ ของ manager ฝั่งขวาเกิดข้อยกเว้น __exit__ ของ manager ฝั่งซ้ายก็ยังทำงาน

2.24.2. การเขียน context manager

คลาสใดก็ตามที่มีเมธอด __enter__ และ __exit__ สามารถใช้งานเป็น context manager ได้:

class Section:
    def __init__(self, label):
        self.label = label

    def __enter__(self):
        print("---", self.label, "---")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("--- end", self.label, "---")
        return False

with Section("setup"):
    print("doing the work")

ผลลัพธ์:

--- setup ---
doing the work
--- end setup ---

__exit__ รับอาร์กิวเมนต์สามตัวที่อธิบายข้อยกเว้นที่ทำให้บล็อกสิ้นสุด หรือค่า None สามค่าถ้าบล็อกทำงานสำเร็จ การคืนค่า False (หรือ None) จะปล่อยให้ข้อยกเว้นใดๆ แพร่กระจายหลังการล้างข้อมูล การคืนค่า True จะระงับข้อยกเว้นนั้น

ใช้ context manager สำหรับทรัพยากรใดก็ตามที่มีวงจรชีวิต "เปิด / ปิด" หรือ "ได้รับ / ปล่อย" -- ไม่ใช่แค่ไฟล์ รูปแบบนี้ทำให้การล้างข้อมูลอยู่คู่กับการตั้งค่า ณ จุดที่ทั้งคู่ถูกแนะนำ ดังนั้นการลืมปิดในกลางฟังก์ชันยาวๆ จึงไม่ทำให้ทรัพยากรรั่วไหล