ข้ามไปยังเนื้อหา

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 ทำสิ่งที่คุณสั่งได้หรือเปล่า

TypeScript
// TypeScript: explicit interface required
interface 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 time
function makeSound(animal: Speaker): void {
console.log(animal.speak());
}
makeSound(new Dog());
makeSound(new Cat());
Python
# Python: no interface needed — just call the method
class 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() exists
def 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() ใช้ได้ทันที โดยไม่ต้องประกาศอะไรเลย

ความแตกต่างเชิงโครงสร้างนี้ขยายไปถึงการจัดการ error นักพัฒนา TypeScript มักใช้ Look Before You Leap (LBYL): ตรวจสอบว่า operation ปลอดภัยก่อนที่จะลงมือ วัฒนธรรม Python ชอบ Easier to Ask Forgiveness than Permission (EAFP): ลองทำเลย แล้วค่อยจัดการ exception ถ้าล้มเหลว

TypeScript
// TypeScript: LBYL — check first, then act
function getValue(obj: Record<string, unknown>, key: string): unknown {
if (key in obj) {
return obj[key];
}
return undefined;
}
// Also common in TypeScript
function parseNumber(s: string): number | null {
const n = Number(s);
return isNaN(n) ? null : n;
}
Python
# Python: EAFP — try it and handle failure
def get_value(d: dict, key: str):
try:
return d[key]
except KeyError:
return None
# LBYL equivalent works too, but is less idiomatic
def get_value_lbyl(d: dict, key: str):
if key in d:
return d[key]
return None
# EAFP for type conversion
def 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 pattern
def 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"))
"duck typing" ใน Python คืออะไร?
EAFP ย่อมาจากอะไร?
ใน Python ถ้าคุณเรียก `.speak()` บน object ที่ไม่มี method นั้น จะเกิดอะไรขึ้น?
วัฒนธรรม Python ชอบ pattern ไหนสำหรับการเข้าถึง dictionary key ที่อาจไม่มีอยู่?