Special Methods (Magic Methods)
Special Methods là gì?
Phần tiêu đề “Special Methods là gì?”Special Methods (còn gọi là Magic Methods hoặc Dunder Methods) là các phương thức đặc biệt trong Python với tên bắt đầu và kết thúc bằng __ (double underscore).
Ví dụ: __init__, __str__, __add__, __len__
Tại sao gọi là “Magic”?
Phần tiêu đề “Tại sao gọi là “Magic”?”Các phương thức này được Python gọi tự động trong một số tình huống đặc biệt:
__init__()được gọi khi tạo object__str__()được gọi khi dùngprint()__add__()được gọi khi dùng toán tử+__len__()được gọi khi dùng hàmlen()
Các Special Methods phổ biến
Phần tiêu đề “Các Special Methods phổ biến”1. Object Initialization và Representation
Phần tiêu đề “1. Object Initialization và Representation”__init__(self, ...) - Constructor
Phần tiêu đề “__init__(self, ...) - Constructor”Khởi tạo object khi tạo mới.
class Person: def __init__(self, name, age): self.name = name self.age = age
person = Person("An", 20) # __init__ được gọi tự động__str__(self) - String Representation (cho người dùng)
Phần tiêu đề “__str__(self) - String Representation (cho người dùng)”Trả về chuỗi khi dùng print() hoặc str().
class Person: def __init__(self, name, age): self.name = name self.age = age
def __str__(self): return f"{self.name}, {self.age} tuổi"
person = Person("An", 20)print(person) # An, 20 tuổi__repr__(self) - Official Representation (cho lập trình viên)
Phần tiêu đề “__repr__(self) - Official Representation (cho lập trình viên)”Trả về chuỗi đại diện chính thức của object.
class Person: def __init__(self, name, age): self.name = name self.age = age
def __repr__(self): return f"Person(name='{self.name}', age={self.age})"
def __str__(self): return f"{self.name}, {self.age} tuổi"
person = Person("An", 20)print(person) # An, 20 tuổi (__str__)print(repr(person)) # Person(name='An', age=20) (__repr__)2. Comparison Operators (Toán tử so sánh)
Phần tiêu đề “2. Comparison Operators (Toán tử so sánh)”class Student: def __init__(self, name, gpa): self.name = name self.gpa = gpa
def __eq__(self, other): """Equal: ==""" return self.gpa == other.gpa
def __ne__(self, other): """Not equal: !=""" return self.gpa != other.gpa
def __lt__(self, other): """Less than: <""" return self.gpa < other.gpa
def __le__(self, other): """Less than or equal: <=""" return self.gpa <= other.gpa
def __gt__(self, other): """Greater than: >""" return self.gpa > other.gpa
def __ge__(self, other): """Greater than or equal: >=""" return self.gpa >= other.gpa
def __str__(self): return f"{self.name} (GPA: {self.gpa})"
s1 = Student("An", 3.5)s2 = Student("Bình", 3.2)s3 = Student("Cường", 3.5)
print(s1 == s3) # True (__eq__)print(s1 != s2) # True (__ne__)print(s1 > s2) # True (__gt__)print(s1 <= s3) # True (__le__)3. Arithmetic Operators (Toán tử số học)
Phần tiêu đề “3. Arithmetic Operators (Toán tử số học)”class Vector: def __init__(self, x, y): self.x = x self.y = y
def __add__(self, other): """Addition: +""" return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other): """Subtraction: -""" return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar): """Multiplication: *""" if isinstance(scalar, (int, float)): return Vector(self.x * scalar, self.y * scalar) raise TypeError("Phải nhân với số")
def __truediv__(self, scalar): """Division: /""" if isinstance(scalar, (int, float)): return Vector(self.x / scalar, self.y / scalar) raise TypeError("Phải chia cho số")
def __str__(self): return f"Vector({self.x}, {self.y})"
v1 = Vector(4, 2)v2 = Vector(2, 1)
print(v1 + v2) # Vector(6, 3)print(v1 - v2) # Vector(2, 1)print(v1 * 3) # Vector(12, 6)print(v1 / 2) # Vector(2.0, 1.0)4. Container Methods
Phần tiêu đề “4. Container Methods”__len__(self) - Length
Phần tiêu đề “__len__(self) - Length”class Playlist: def __init__(self, name): self.name = name self.songs = []
def add_song(self, song): self.songs.append(song)
def __len__(self): return len(self.songs)
playlist = Playlist("My Favorites")playlist.add_song("Song 1")playlist.add_song("Song 2")playlist.add_song("Song 3")
print(len(playlist)) # 3__getitem__(self, key) và __setitem__(self, key, value) - Indexing
Phần tiêu đề “__getitem__(self, key) và __setitem__(self, key, value) - Indexing”class ShoppingCart: def __init__(self): self.items = []
def add_item(self, item): self.items.append(item)
def __getitem__(self, index): """Cho phép cart[index]""" return self.items[index]
def __setitem__(self, index, value): """Cho phép cart[index] = value""" self.items[index] = value
def __len__(self): return len(self.items)
cart = ShoppingCart()cart.add_item("Laptop")cart.add_item("Mouse")cart.add_item("Keyboard")
print(cart[0]) # Laptop (__getitem__)print(cart[1]) # Mouse
cart[1] = "Gaming Mouse" # __setitem__print(cart[1]) # Gaming Mouse
# Có thể dùng trong vòng lặpfor item in cart: print(item)__contains__(self, item) - Membership Test
Phần tiêu đề “__contains__(self, item) - Membership Test”class Team: def __init__(self, name): self.name = name self.members = []
def add_member(self, member): self.members.append(member)
def __contains__(self, member): """Cho phép dùng 'in'""" return member in self.members
team = Team("Dev Team")team.add_member("An")team.add_member("Bình")
print("An" in team) # Trueprint("Cường" in team) # False5. Iteration
Phần tiêu đề “5. Iteration”__iter__(self) và __next__(self) - Iterator Protocol
Phần tiêu đề “__iter__(self) và __next__(self) - Iterator Protocol”class Countdown: def __init__(self, start): self.start = start
def __iter__(self): self.current = self.start return self
def __next__(self): if self.current <= 0: raise StopIteration self.current -= 1 return self.current + 1
# Sử dụngcountdown = Countdown(5)for num in countdown: print(num) # 5, 4, 3, 2, 16. Context Manager
Phần tiêu đề “6. Context Manager”__enter__(self) và __exit__(self) - Dùng với with
Phần tiêu đề “__enter__(self) và __exit__(self) - Dùng với with”class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None
def __enter__(self): """Được gọi khi vào block with""" self.file = open(self.filename, self.mode) return self.file
def __exit__(self, exc_type, exc_val, exc_tb): """Được gọi khi ra khỏi block with""" if self.file: self.file.close()
# Sử dụngwith FileManager('test.txt', 'w') as f: f.write('Hello World')# File tự động đóng sau khi ra khỏi block7. Callable Objects
Phần tiêu đề “7. Callable Objects”__call__(self) - Gọi object như hàm
Phần tiêu đề “__call__(self) - Gọi object như hàm”class Multiplier: def __init__(self, factor): self.factor = factor
def __call__(self, value): """Cho phép gọi object như hàm""" return value * self.factor
double = Multiplier(2)triple = Multiplier(3)
print(double(5)) # 10print(triple(5)) # 15Ví dụ thực tế: Class Money
Phần tiêu đề “Ví dụ thực tế: Class Money”class Money: def __init__(self, amount, currency="VND"): self.amount = amount self.currency = currency
def __str__(self): """String representation""" return f"{self.amount:,.0f} {self.currency}"
def __repr__(self): """Official representation""" return f"Money({self.amount}, '{self.currency}')"
def __add__(self, other): """Addition""" if isinstance(other, Money): if self.currency != other.currency: raise ValueError("Không thể cộng hai đơn vị tiền tệ khác nhau!") return Money(self.amount + other.amount, self.currency) elif isinstance(other, (int, float)): return Money(self.amount + other, self.currency) raise TypeError("Không thể cộng Money với kiểu này")
def __sub__(self, other): """Subtraction""" if isinstance(other, Money): if self.currency != other.currency: raise ValueError("Không thể trừ hai đơn vị tiền tệ khác nhau!") return Money(self.amount - other.amount, self.currency) elif isinstance(other, (int, float)): return Money(self.amount - other, self.currency) raise TypeError("Không thể trừ Money với kiểu này")
def __mul__(self, scalar): """Multiplication""" if isinstance(scalar, (int, float)): return Money(self.amount * scalar, self.currency) raise TypeError("Chỉ có thể nhân Money với số")
def __truediv__(self, scalar): """Division""" if isinstance(scalar, (int, float)): return Money(self.amount / scalar, self.currency) raise TypeError("Chỉ có thể chia Money cho số")
def __eq__(self, other): """Equal""" if isinstance(other, Money): return self.amount == other.amount and self.currency == other.currency return False
def __lt__(self, other): """Less than""" if isinstance(other, Money): if self.currency != other.currency: raise ValueError("Không thể so sánh hai đơn vị tiền tệ khác nhau!") return self.amount < other.amount raise TypeError("Không thể so sánh Money với kiểu này")
def __le__(self, other): """Less than or equal""" return self == other or self < other
def __bool__(self): """Boolean conversion""" return self.amount > 0
# Sử dụngm1 = Money(100000)m2 = Money(50000)
print(m1) # 100,000 VNDprint(repr(m1)) # Money(100000, 'VND')
m3 = m1 + m2print(m3) # 150,000 VND
m4 = m1 - m2print(m4) # 50,000 VND
m5 = m1 * 2print(m5) # 200,000 VND
m6 = m1 / 2print(m6) # 50,000 VND
print(m1 > m2) # Trueprint(m1 == m2) # False
if m1: print("Có tiền!") # Có tiền!Ví dụ: Class Range (tự implement)
Phần tiêu đề “Ví dụ: Class Range (tự implement)”class MyRange: """Tự implement class tương tự range() của Python""" def __init__(self, start, stop=None, step=1): if stop is None: self.start = 0 self.stop = start else: self.start = start self.stop = stop self.step = step
def __iter__(self): self.current = self.start return self
def __next__(self): if (self.step > 0 and self.current >= self.stop) or \ (self.step < 0 and self.current <= self.stop): raise StopIteration value = self.current self.current += self.step return value
def __len__(self): return max(0, (self.stop - self.start + self.step - 1) // self.step)
def __getitem__(self, index): if index < 0 or index >= len(self): raise IndexError("Index out of range") return self.start + index * self.step
def __contains__(self, value): if self.step > 0: return self.start <= value < self.stop and \ (value - self.start) % self.step == 0 else: return self.stop < value <= self.start and \ (value - self.start) % self.step == 0
def __str__(self): return f"MyRange({self.start}, {self.stop}, {self.step})"
# Sử dụngr = MyRange(0, 10, 2)
print(r) # MyRange(0, 10, 2)print(len(r)) # 5print(r[2]) # 4print(4 in r) # Trueprint(5 in r) # False
for num in r: print(num) # 0, 2, 4, 6, 8Bảng tổng hợp Special Methods
Phần tiêu đề “Bảng tổng hợp Special Methods”| Loại | Method | Mô tả |
|---|---|---|
| Khởi tạo | __init__(self, ...) |
Constructor |
__new__(cls, ...) |
Tạo instance mới | |
__del__(self) |
Destructor | |
| Representation | __str__(self) |
str(), print() |
__repr__(self) |
repr(), representation chính thức |
|
| So sánh | __eq__(self, other) |
== |
__ne__(self, other) |
!= |
|
__lt__(self, other) |
< |
|
__le__(self, other) |
<= |
|
__gt__(self, other) |
> |
|
__ge__(self, other) |
>= |
|
| Toán tử số học | __add__(self, other) |
+ |
__sub__(self, other) |
- |
|
__mul__(self, other) |
* |
|
__truediv__(self, other) |
/ |
|
__floordiv__(self, other) |
// |
|
__mod__(self, other) |
% |
|
__pow__(self, other) |
** |
|
| Container | __len__(self) |
len() |
__getitem__(self, key) |
obj[key] |
|
__setitem__(self, key, value) |
obj[key] = value |
|
__delitem__(self, key) |
del obj[key] |
|
__contains__(self, item) |
in |
|
| Iteration | __iter__(self) |
Iterator |
__next__(self) |
Next item | |
| Callable | __call__(self, ...) |
obj() |
| Context Manager | __enter__(self) |
with statement entry |
__exit__(self, ...) |
with statement exit |
|
| Attribute Access | __getattr__(self, name) |
Get attribute |
__setattr__(self, name, value) |
Set attribute | |
__delattr__(self, name) |
Delete attribute |
Tổng kết
Phần tiêu đề “Tổng kết”- Special Methods cho phép class của bạn hoạt động giống built-in types
- Bắt đầu và kết thúc bằng
__(dunder - double underscore) - Được Python gọi tự động trong các tình huống đặc biệt
- Giúp code trở nên Pythonic và trực quan hơn
- Cho phép overload operators và implement protocols
Chúc mừng bạn đã hoàn thành series về OOP trong Python! 🎉