Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

ยูทิลิตี้สำหรับบริบท with-statement ใน Python (contextlib)

โมดูล 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 สำหรับการจัดการทรัพยากรในโปรแกรมอย่างมีประสิทธิภาพ