0Pricing
Python Academy · บทเรียน

โพรโทคอล __enter__ และ __exit__

สร้างตัวจัดการบริบทด้วยโพรโทคอล dunder

โพรโทคอล __enter__ และ __exit__ เป็นบทเรียน Python Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Python Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

โพรโทคอลตัวจัดการบริบท

อ็อบเจ็กต์ใดก็ตามที่กำหนด __enter__ และ __exit__ จะเป็นตัวจัดการบริบทและสามารถใช้ร่วมกับ with ได้

class Managed:
    def __enter__(self):
        print("entering")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exiting")
        return False

with Managed() as m:
    print("inside")

ค่าที่ส่งคืนโดย __enter__

__enter__ จะถูกเรียกเมื่อเริ่มต้นบล็อก with ค่าที่ส่งคืนจะถูกผูกกับตัวแปรหลัง as

class Resource:
    def __enter__(self):
        self.value = 42
        return self  # bound to 'r'

    def __exit__(self, *args):
        self.value = None
        return False

with Resource() as r:
    print(r.value)  # 42

อาร์กิวเมนต์ของ __exit__

__exit__(exc_type, exc_val, exc_tb) จะได้รับข้อมูลข้อยกเว้นหากเกิดข้อผิดพลาด หากไม่มีข้อยกเว้น ทั้งสามค่าจะเป็น None

class Safe:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"Suppressed ValueError: {exc_val}")
            return True   # suppress the exception
        return False      # re-raise anything else

with Safe():
    raise ValueError("oops")  # suppressed

การระงับข้อยกเว้น

คืนค่า True จาก __exit__ เพื่อระงับข้อยกเว้น คืนค่า False หรือไม่คืนค่าใด ๆ เพื่อให้ข้อยกเว้นถูกส่งต่อ

class Ignore:
    def __enter__(self): return self
    def __exit__(self, *args): return True  # swallow all exceptions

with Ignore():
    1 / 0

print("continues here")

ตัวอย่างการเชื่อมต่อฐานข้อมูล

กรณีใช้งานแบบคลาสสิกคือ ขอการเชื่อมต่อเมื่อเข้าสู่บริบท จากนั้น commit หรือ rollback และปิดการเชื่อมต่อเมื่อออกจากบริบท

class DBConn:
    def __enter__(self):
        self.conn = connect_db()
        return self.conn

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            self.conn.rollback()
        else:
            self.conn.commit()
        self.conn.close()
        return False

ตัวจัดการบริบท Timer

วัดเวลาที่ผ่านไปโดยบันทึกเวลาเริ่มต้นใน __enter__ และคำนวณส่วนต่างใน __exit__

import time

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self

    def __exit__(self, *args):
        self.elapsed = time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.3f}s")
        return False

with Timer():
    sum(range(1_000_000))

การซ้อนตัวจัดการบริบท

สามารถรวมตัวจัดการบริบทหลายตัวไว้ในบรรทัด with เดียวได้

with open("in.txt") as src, open("out.txt", "w") as dst:
    dst.write(src.read())

ตัวจัดการที่ใช้ซ้ำได้กับตัวจัดการที่ใช้ครั้งเดียว

ตัวจัดการบริบทที่ __enter__ จัดสรรทรัพยากรใหม่ทุกครั้งจะสามารถใช้ซ้ำได้ ส่วนตัวจัดการที่ใช้สถานะภายในจนหมด เช่น ล็อกที่ถือได้เพียงครั้งเดียว อาจไม่สามารถใช้ซ้ำได้

class Reusable:
    def __enter__(self):
        return []
    def __exit__(self, *a):
        return False

for _ in range(3):
    with Reusable() as lst:
        lst.append(1)
        print(lst)  # [1] each time

__aenter__ และ __aexit__ (แบบอะซิงก์)

สำหรับตัวจัดการบริบทแบบอะซิงก์ ให้กำหนดคอรูทีน __aenter__ และ __aexit__ และใช้ async with

class AsyncDB:
    async def __aenter__(self):
        self.conn = await connect()
        return self.conn

    async def __aexit__(self, *args):
        await self.conn.close()

# async with AsyncDB() as conn:
#     ...

การครอบอ็อบเจ็กต์ไฟล์

อ็อบเจ็กต์ไฟล์ของ Python มีการใช้งานโพรโทคอลตัวจัดการบริบทอยู่แล้ว จึงไม่ควรเรียก f.close() ด้วยตนเอง

with open("data.txt", "r") as f:
    data = f.read()
# file is closed here automatically
print(f.closed)  # True

การใช้งาน Lock

ล็อกของเธรดเป็นตัวจัดการบริบท: __enter__ จะยึดล็อก และ __exit__ จะปล่อยล็อก

import threading

lock = threading.Lock()

def update_shared():
    with lock:
        # critical section
        pass

ตรวจสอบอย่างรวดเร็ว

ควรส่งคืนค่าใดจาก __exit__ เพื่อระงับข้อยกเว้น

สรุป

นำ __enter__ และ __exit__ มาใช้เพื่อสร้างตัวจัดการบริบท ส่งคืนทรัพยากรจาก __enter__ และจัดการการทำความสะอาดรวมถึงการระงับข้อยกเว้นใน __exit__

คำถามที่พบบ่อย

บทเรียน “โพรโทคอล __enter__ และ __exit__” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “โพรโทคอล __enter__ และ __exit__” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Python Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “โพรโทคอล __enter__ และ __exit__”

สร้างตัวจัดการบริบทด้วยโพรโทคอล dunder คุณปฏิบัติ Python Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Python Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Python Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “โพรโทคอล __enter__ และ __exit__” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Python Academy นี้ได้ไหม

ได้ บทเรียน Python Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เจาะลึกคำสั่ง with
  2. โพรโทคอล __enter__ และ __exit__
  3. contextlib: @contextmanager
  4. รูปแบบการใช้ตัวจัดการบริบทในทางปฏิบัติ
← กลับไปที่ Python Academy