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

Dunder Methods (เมธอดมหัศจรรย์)

TypeScript ไม่รองรับ operator overloading เลย ถ้าคุณสร้าง class Vector คุณต้องเรียกเมธอด .add(other) โดยตรง — การเขียน v1 + v2 ไม่มีทางใช้ได้กับ type ที่กำหนดเอง Python แก้ปัญหานี้ผ่าน dunder methods (ย่อมาจาก “double underscore”) ซึ่งเรียกอีกชื่อว่า magic methods เมธอดเหล่านี้คือเมธอดธรรมดาที่มีชื่ออย่าง __add__, __str__, และ __len__ ซึ่ง Python จะเรียกโดยอัตโนมัติเมื่อคุณใช้ operator หรือ built-in function กับ object ของคุณ

คำว่า “dunder” เป็นคำแสลงในชุมชน Python ที่ใช้เรียกชื่อที่ขึ้นต้นและลงท้ายด้วย underscore สองตัว คุณนิยามเมธอดเหล่านี้เอง และ Python จะเรียกใช้ให้เบื้องหลัง

ตัวอย่างที่อธิบายได้ชัดเจนที่สุดคือ vector 2 มิติ ใน TypeScript คุณต้องตั้งชื่อ operation อย่างชัดเจน ส่วนใน Python คุณเชื่อมโยงกับ + ได้โดยการนิยาม __add__

TypeScript
// TypeScript: no operator overloading — you must name the method
class Vector {
constructor(public x: number, public y: number) {}
toString(): string {
return `Vector(${this.x}, ${this.y})`;
}
add(other: Vector): Vector {
return new Vector(this.x + other.x, this.y + other.y);
}
equals(other: Vector): boolean {
return this.x === other.x && this.y === other.y;
}
get length(): number {
return 2;
}
}
const v1 = new Vector(1, 2);
const v2 = new Vector(3, 4);
const v3 = v1.add(v2); // must call .add(), can't write v1 + v2
console.log(v3.toString()); // "Vector(4, 6)"
Python
# Python: operator overloading via dunder methods
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x!r}, {self.y!r})"
def __len__(self):
return 2
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # calls v1.__add__(v2) automatically
print(str(v1)) # calls v1.__str__()
print(len(v1)) # calls v1.__len__()
print(v1 == Vector(1, 2)) # calls v1.__eq__(...)

เวลา Python เจอ v1 + v2 เบื้องหลังจะเรียก type(v1).__add__(v1, v2) เรียก str(v1) ก็ไปที่ v1.__str__() และเรียก len(v1) ก็ไปที่ v1.__len__() พูดง่าย ๆ คือ built-in function และ operator แทบทุกตัวมี dunder hook คู่กันไว้ให้คุณเข้าไปกำหนดพฤติกรรมเอง

นอกจากนี้ยังมี __repr__ ซึ่งต่างจาก __str__ คือ str() ออกแบบมาสำหรับการแสดงผลที่มนุษย์อ่านได้ ส่วน repr() (และ REPL แบบ interactive) ใช้ __repr__ สำหรับ output ที่ชัดเจนและมุ่งเน้นนักพัฒนา แฟล็ก !r ใน f-string จะเรียก repr() กับค่านั้น

class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x!r}, {self.y!r})"
def __len__(self):
return 2
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2
print(str(v1))
print(len(v1))
print(v1 == Vector(1, 2))
print(v3)
Python เรียก dunder method ใดเมื่อคุณเขียน `v1 + v2`?
`__str__` และ `__repr__` ต่างกันอย่างไร?
dunder methods คู่ใดที่ต้องนำมาใช้เพื่อรองรับคำสั่ง `with` บน class ที่กำหนดเอง?
เกิดอะไรขึ้นกับ `__hash__` เมื่อคุณนิยาม `__eq__` บน class?