Context Managers และ with
try/finally ใน TypeScript, with ใน Python
หัวข้อที่มีชื่อว่า “try/finally ใน TypeScript, with ใน Python”ใน TypeScript ถ้าอยากมั่นใจว่า resource ได้คืนแน่นอนไม่ว่าจะเกิด exception หรือไม่ คุณต้องเขียนบล็อก try/finally เอง
Python ใช้แนวคิดเดียวกัน แต่ยกขึ้นมาเป็น syntax ระดับภาษาผ่าน with statement ตัว with การันตีว่า resource setup และ teardown ครบถ้วน ต่อให้โค้ดข้างในจะ raise exception ก็ตาม โดยที่คุณไม่ต้องมานั่งเขียน try/finally ซ้ำ ๆ ทุกครั้ง
ตัวอย่างขั้นพื้นฐาน: การเปิดไฟล์
หัวข้อที่มีชื่อว่า “ตัวอย่างขั้นพื้นฐาน: การเปิดไฟล์”การเปิดไฟล์คือตัวอย่างที่พบบ่อยที่สุด TypeScript ไม่มี file API ระดับภาษาในตัว แต่ pattern จะมีลักษณะดังนี้:
// TypeScript: manual try/finally for resource cleanupimport * as fs from "fs";
function readFile(path: string): string { const fd = fs.openSync(path, "r"); try { const buf = Buffer.alloc(1024); const bytesRead = fs.readSync(fd, buf, 0, 1024, 0); return buf.toString("utf8", 0, bytesRead); } finally { fs.closeSync(fd); // always runs }}# Python: with statement handles open/close automaticallydef read_file(path): with open(path, "r") as f: return f.read() # f.close() is called automatically — even if read() raisesบล็อก with เรียก f.close() โดยอัตโนมัติเมื่อออกจากบล็อก ไม่ว่าจะออกปกติหรือเพราะ exception ไม่ต้องมี try/finally ที่ซ้ำซ้อน
การสร้าง context manager ด้วย class
หัวข้อที่มีชื่อว่า “การสร้าง context manager ด้วย class”object ใดก็ตามที่ implement เมธอด __enter__ และ __exit__ สามารถใช้กับ with statement ได้ นี่คือ context manager protocol
// TypeScript: class-based resource managementclass ManagedResource { constructor(private name: string) {}
acquire(): this { console.log(`Acquiring ${this.name}`); return this; }
release(): void { console.log(`Releasing ${this.name}`); }
use<T>(fn: (r: this) => T): T { this.acquire(); try { return fn(this); } finally { this.release(); } }}
new ManagedResource("database connection").use(resource => { console.log(`Using ${resource}`);});# Python: __enter__ and __exit__ make any class a context managerclass ManagedResource: def __init__(self, name): self.name = name
def __enter__(self): print(f"Acquiring {self.name}") return self # bound to the 'as' variable
def __exit__(self, exc_type, exc_val, exc_tb): print(f"Releasing {self.name}") return False # False = don't suppress exceptions
with ManagedResource("database connection") as resource: print(f"Using ${resource.name}")__exit__ รับ argument สามตัวที่อธิบาย exception ที่เกิดขึ้น (ถ้ามี) คือ type, value และ traceback ถ้าคืน True Python จะกลืน exception นั้นทิ้ง แต่ถ้าคืน False หรือ None exception จะเด้งต่อออกไปตามปกติ
การสร้าง context manager ด้วย @contextmanager
หัวข้อที่มีชื่อว่า “การสร้าง context manager ด้วย @contextmanager”การเขียน class เต็มรูปแบบเพียงเพื่อจัดการ setup/teardown เล็กน้อยอาจดูมากเกินไป @contextmanager decorator จาก contextlib ให้คุณเขียน generator ธรรมดาแทนได้ โค้ดก่อน yield คือ __enter__ โค้ดหลัง yield คือ __exit__
// TypeScript: helper function simulating @contextmanagerfunction withTimer<T>(label: string, fn: () => T): T { console.log(`[${label}] starting`); try { return fn(); } finally { console.log(`[${label}] done`); }}
const result = withTimer("my block", () => { let total = 0; for (let i = 0; i < 1000; i++) total += i; console.log(`sum = ${total}`); return total;});# Python: @contextmanager turns a generator into a context managerfrom contextlib import contextmanager
@contextmanagerdef timer(label): print(f"[{label}] starting") yield # control passes to the 'with' block here print(f"[{label}] done")
with timer("my block"): total = sum(range(1000)) print(f"sum = ${total}")ลองด้วยตัวเอง
หัวข้อที่มีชื่อว่า “ลองด้วยตัวเอง”from contextlib import contextmanager
class ManagedResource: def __init__(self, name): self.name = name
def __enter__(self): print(f"Acquiring {self.name}") return self
def __exit__(self, exc_type, exc_val, exc_tb): print(f"Releasing {self.name}") return False
with ManagedResource("database connection") as resource: print(f"Using {resource.name}")
@contextmanagerdef timer(label): print(f"[{label}] starting") yield print(f"[{label}] done")
with timer("my block"): total = sum(range(1000)) print(f"sum = {total}")Loading Python runtime (first run only)…