Context Managers และ with
TypeScript ไม่มี with — Python มี
หัวข้อที่มีชื่อว่า “TypeScript ไม่มี with — Python มี”TypeScript ไม่มี keyword with เมื่อต้องการยึด resource แล้วการันตีว่ามันถูกคืนแน่นอน ไม่ว่าจะเป็น file handle, database connection, หรือ lock คุณต้องเขียน try/finally เอง ตั้ง resource ก่อน try แล้วเก็บกวาดใน finally วิธีนี้ใช้ได้ แต่ต้องจำ pattern และเขียนให้ถูกต้องทุกครั้ง
Python’s with statement ห่อหุ้ม setup และ teardown ไว้ในตัว object เอง object กำหนดว่าจะเกิดอะไรตอนเข้าและตอนออก และ Python การันตีว่า exit จะทำงาน — แม้จะมี exception เกิดขึ้นในบล็อกก็ตาม logic การเก็บกวาดอยู่ที่ resource ไม่กระจัดกระจายไปทุกจุดที่เรียกใช้
ตัวอย่างขั้นพื้นฐาน: การเปิดไฟล์
หัวข้อที่มีชื่อว่า “ตัวอย่างขั้นพื้นฐาน: การเปิดไฟล์”การเปิดไฟล์คือตัวอย่างที่พบบ่อยที่สุด 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”class จะกลายเป็น context manager ได้โดย implement เมธอด dunder สองตัว: __enter__ และ __exit__ Python เรียก __enter__ ตอนเข้าบล็อก with และเรียก __exit__ ตอนออก — ไม่ว่าจะออกด้วยเหตุผลใดก็ตาม
// TypeScript: try/finally for guaranteed cleanupclass ManagedResource { constructor(private name: string) {}
acquire(): this { console.log(`Acquiring ${this.name}`); return this; }
release(): void { console.log(`Releasing ${this.name}`); }
use(): void { console.log(`Using ${this.name}`); }}
const resource = new ManagedResource("database connection");resource.acquire();try { resource.use();} finally { resource.release(); // must remember to call this}# Python: with statement handles acquire and release automaticallyclass 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 # do not suppress exceptions
with ManagedResource("database connection") as resource: print(f"Using {resource.name}")# release is guaranteed even if an exception occursค่าที่ __enter__ return จะถูก bind เข้ากับตัวแปรหลัง as ส่วน as clause นั้นไม่บังคับ — เขียนแค่ with ManagedResource("x"): ก็ได้ถ้าไม่ต้องการอ้างอิงถึง resource ในบล็อก
contextlib: generator ที่เป็น context manager
หัวข้อที่มีชื่อว่า “contextlib: generator ที่เป็น context manager”การเขียน class เต็มรูปแบบเพียงเพื่อ context manager ง่าย ๆ มักดูมากเกินไป โมดูล contextlib มี decorator @contextmanager ให้คุณเขียน context manager เป็น generator function แทนได้ ทุกอย่างก่อน yield คือ setup ทุกอย่างหลัง yield คือ teardown
// TypeScript: a utility wrapper function with try/finallyfunction 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;});from contextlib import contextmanager
@contextmanagerdef timer(label): print(f"[{label}] starting") yield # execution of the with block happens 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)…