Duck Typing และ EAFP
มองโครงสร้างต่างมุม
หัวข้อที่มีชื่อว่า “มองโครงสร้างต่างมุม”ทั้ง TypeScript และ Python เป็นภาษา structurally typed คือค่าหนึ่งจะเข้ากับ type หนึ่งได้ก็ต่อเมื่อมี shape ตรงกัน แต่กลไกเบื้องหลังของสองภาษาต่างกันคนละเรื่อง
TypeScript ตรวจสอบโครงสร้าง ตอน compile ผ่าน interface และ type aliases ถ้าค่าไม่ตรงตาม interface compiler จะปฏิเสธก่อนที่ code จะรัน Python ตรวจสอบโครงสร้าง ตอน runtime โดยลอง call method หรือเข้าถึง attribute โดยตรง ถ้า object มีสิ่งนั้น ก็ดำเนินต่อไป ถ้าไม่มี จะได้รับ AttributeError
แนวทางแบบ runtime นี้เรียกว่า duck typing คือถ้ามันเดินเหมือนเป็ดและร้องเหมือนเป็ด ก็ถือว่าเป็นเป็ด Python ไม่สนใจว่าประกาศ type ไว้ว่าอะไร สนแค่ว่า object ทำสิ่งที่คุณสั่งได้หรือเปล่า
Duck typing: interface vs behavior
หัวข้อที่มีชื่อว่า “Duck typing: interface vs behavior”// TypeScript: explicit interface requiredinterface Speaker { speak(): string;}
class Dog implements Speaker { speak(): string { return "Woof!"; }}
class Cat implements Speaker { speak(): string { return "Meow!"; }}
// TypeScript verifies the interface at compile timefunction makeSound(animal: Speaker): void { console.log(animal.speak());}
makeSound(new Dog());makeSound(new Cat());# Python: no interface needed — just call the methodclass Dog: def speak(self) -> str: return "Woof!"
class Cat: def speak(self) -> str: return "Meow!"
class Duck: def speak(self) -> str: return "Quack!"
# Python checks at runtime whether .speak() existsdef make_sound(animal) -> None: print(animal.speak())
make_sound(Dog())make_sound(Cat())make_sound(Duck()) # works — Duck was never declared as Speakerใน TypeScript Duck ต้องประกาศ implement Speaker อย่างชัดเจน ใน Python object ใดก็ตามที่มี method .speak() ใช้ได้ทันที โดยไม่ต้องประกาศอะไรเลย
LBYL กับ EAFP
หัวข้อที่มีชื่อว่า “LBYL กับ EAFP”ความแตกต่างเชิงโครงสร้างนี้ขยายไปถึงการจัดการ error นักพัฒนา TypeScript มักใช้ Look Before You Leap (LBYL): ตรวจสอบว่า operation ปลอดภัยก่อนที่จะลงมือ วัฒนธรรม Python ชอบ Easier to Ask Forgiveness than Permission (EAFP): ลองทำเลย แล้วค่อยจัดการ exception ถ้าล้มเหลว
// TypeScript: LBYL — check first, then actfunction getValue(obj: Record<string, unknown>, key: string): unknown { if (key in obj) { return obj[key]; } return undefined;}
// Also common in TypeScriptfunction parseNumber(s: string): number | null { const n = Number(s); return isNaN(n) ? null : n;}# Python: EAFP — try it and handle failuredef get_value(d: dict, key: str): try: return d[key] except KeyError: return None
# LBYL equivalent works too, but is less idiomaticdef get_value_lbyl(d: dict, key: str): if key in d: return d[key] return None
# EAFP for type conversiondef parse_number(s: str): try: return int(s) except ValueError: return Noneลองด้วยตัวเอง
หัวข้อที่มีชื่อว่า “ลองด้วยตัวเอง”class Dog: def speak(self): return "Woof!"
class Cat: def speak(self): return "Meow!"
class Duck: def speak(self): return "Quack!"
def make_sound(animal): print(animal.speak())
for animal in [Dog(), Cat(), Duck()]: make_sound(animal)
# EAFP patterndef get_value(d, key): try: return d[key] except KeyError: return "not found"
data = {"name": "Alice", "age": 30}print(get_value(data, "name"))print(get_value(data, "email"))Loading Python runtime (first run only)…