Một trong những yếu tố góp phần khiến Python trở thành ngôn ngữ lập trình mạnh mẽ là các magic methods. Trong một số cộng đồng Pythonista, bạn có thể nghe chúng được gọi là ‘dunder methods’ do cấu trúc hai dấu gạch dưới.
Vai trò của chúng trong Python là thực hiện một quy trình gọi là ‘quá tải toán tử’ (operator overloading), nghĩa là một toán tử được định nghĩa sẵn được gán thêm ý nghĩa mở rộng. Nói cách khác, operator overloading được dùng để bổ sung hành vi tùy chỉnh cho các lớp (class) của chúng ta nhằm cho phép chúng hoạt động với các toán tử và chức năng dựng sẵn của Python.
Trong bài viết này, chúng ta sẽ đi sâu hơn vào magic methods trong Python bằng cách:
Phân biệt sự khác nhau giữa method và function
Giải thích magic methods là gì
Cung cấp một số ví dụ Python.
Xem sổ tay DataLab này để làm theo đoạn mã dùng trong hướng dẫn.
Phương thức (Method) và Hàm (Function) trong Python
Trước khi tìm hiểu magic methods là gì, chúng ta cần hiểu sự khác nhau giữa method và function trong Python.
Python dùng method để biểu diễn hành vi của một đối tượng (object). Vì vậy, method gắn với một đối tượng và sẽ thực hiện một số thao tác trên dữ liệu bên trong đối tượng đó. Một yếu tố phân biệt quan trọng khác của method là tham số self phải luôn xuất hiện như tham số đầu tiên được truyền vào method.
Lưu ý: Tham số đầu tiên không bắt buộc phải đặt tên là self, nhưng cộng đồng Pythonista thống nhất đây là thực hành tốt nhất.
# Example of a methodclassHuman:# This is a methoddefwalk(self):print("I am walking")Được hỗ trợ bởi
Các method phụ thuộc vào class mà chúng gắn với. Do đó, để gọi một method, cần tạo một thể hiện (instance) của class trước khi có thể truy cập method bằng toán tử chấm.
# This will work
joe = Human()
joe.walk()"""
I am walking
"""# This will throw a NameError
walk()---------------------------------------------------------------------------
NameError Traceback (most recent call last)/tmp/ipykernel_153/2327344440.py in<module>45# This will throw a NameError---->6 walk()
NameError: name 'walk'isnot definedĐược hỗ trợ bởi
Một function thì khác: chúng ta không phải tạo instance của class.
Hàm trong Python là các bước được thực thi như một khối mã chỉ khi được gọi. Chúng được định nghĩa độc lập với bất kỳ đối tượng class nào, nghĩa là có thể gọi trực tiếp bằng tên. Vì không gắn với class nào, chúng ta không cần truyền self làm tham số – các tham số là tùy chọn.
Tóm lại, function đơn giản là một tập hợp các chỉ dẫn được xác định rõ ràng mà trình thông dịch dùng để thực hiện một tác vụ.
# Example of a functiondefrun():print("I am running.")
run()"""
I am running.
"""Được hỗ trợ bởi
Như đã trình bày trong bài Cách gọi và viết hàm, function cũng có hai dạng: dựng sẵn và do người dùng định nghĩa.
Hàm dựng sẵn: Các hàm có trong thư viện chuẩn của Python.
Hàm do người dùng định nghĩa: Các hàm do người dùng định nghĩa – Python cho phép người dùng tự định nghĩa và sử dụng hàm của riêng mình để truy cập trong chương trình Python.
Ở nhiều khía cạnh, method và function giống nhau – ví dụ, cả hai đều được định nghĩa bằng câu lệnh def. Điểm khác biệt cốt lõi là method gắn với một đối tượng và dùng để thao tác trên dữ liệu nằm trong một class, trong khi function được định nghĩa độc lập và có thể được gọi bằng tên ở bất cứ đâu trong chương trình.
Python magic methods là gì?
Ở phần đầu hướng dẫn, chúng tôi đề cập rằng magic methods còn được gọi là dunder methods do cấu trúc hai dấu gạch dưới. Ý chúng tôi là tất cả magic methods trong Python đều bắt đầu và kết thúc bằng hai dấu gạch dưới.
Magic method nổi tiếng nhất là phương thức __init__, cho phép khởi tạo một class với các thuộc tính cụ thể.
classEmployee:def__init__(self, name, age, role):
self.name = name
self.age = age
self.role = roleĐược hỗ trợ bởi
Để tạo một instance của class này, chúng ta phải chỉ định các thuộc tính name, age và role của instance. Ví dụ:
user_1 = Employee(name="John", age=24, role="Data Scientist")Được hỗ trợ bởi
Phương thức __init__ thuộc nhóm magic methods khởi tạo và dựng (initialization and construction), nhưng nó chỉ là một trong số nhiều phương thức. Bảng dưới đây nêu một số magic methods khác thuộc nhóm khởi tạo và dựng.
__new__(cls, other)
Dùng để tạo một instance mới của class
__init__(self, other)
Được gọi sau khi một instance mới được tạo; Tất cả thuộc tính truyền vào biểu thức khởi tạo class sẽ được dùng làm thuộc tính.
__del__(self)
Còn gọi là destructor; Được gọi khi một instance sắp bị hủy.
Cách đơn giản nhất để hình dung magic methods là một bản hợp đồng giữa phần triển khai của bạn và trình thông dịch Python. Một trong các điều khoản yêu cầu Python thực hiện một số hành động ngầm khi có những điều kiện nhất định, chẳng hạn như khởi tạo một instance của class với các đối tượng cụ thể.
Nói cách khác, bạn không có trách nhiệm gọi trực tiếp magic method – Python sẽ làm điều đó cho bạn ở hậu trường.
Các phương thức biểu diễn chuỗi
Python là một ngôn ngữ lập trình hướng đối tượng (OOP) – mọi thứ trong Python đều là đối tượng. Khi một đối tượng mới được tạo, chúng ta ngầm tạo ra một đối tượng liên quan vì mọi class đều kế thừa từ object.
Lưu ý: bạn cũng có thể làm cho việc kế thừa trở nên tường minh bằng cách truyền class object vào class của bạn.
# This worksclassCar:pass
# This also worksclassVehicle(object):pass
car = Car()
vehicle = Vehicle()
print(car, vehicle, sep="\n")"""
<__main__.Car object at 0x7f80449dc0a0>
<__main__.Vehicle object at 0x7f80449dc1f0>
"""Được hỗ trợ bởi
Trong đoạn mã trên, chúng ta tạo hai class rỗng, nhưng trình thông dịch Python vẫn biết in gì khi gọi lệnh print. Điều này khả thi vì mọi class đều kế thừa từ class object (lớp cha của mọi class trong Python), và nó có một magic method tên là __repr__, mà lệnh print sẽ gọi để trả về một giá trị cho chương trình chính của chúng ta.
Những ai quen thuộc với kế thừa sẽ biết rằng một lớp con có thể ghi đè lớp cha bằng cách định nghĩa method cùng tên của riêng mình.
# Overriding the object __repr__classVehicle:def__repr__(self):returnf"{self.__class__.__qualname__}"
vehicle = Vehicle()print(vehicle)"""
Vehicle
"""Được hỗ trợ bởi
Bạn cũng có thể dùng magic method __str__ để tạo biểu diễn chuỗi được gọi bằng lệnh print.
Thực tế, magic method __repr__ đóng vai trò dự phòng cho magic method __str__. Vì vậy, khi chúng ta gọi lệnh print, Python sẽ tìm __str__ được định nghĩa trong class của bạn trước, rồi mới quay về __repr__ nếu không có.
# Using __str__ for string representationclassVehicle:def__str__(self):returnf"{self.__class__.__qualname__}"
vehicle = Vehicle()print(vehicle)"""
Vehicle
"""Được hỗ trợ bởi
Bảng dưới đây liệt kê một số magic methods khác liên quan đến biểu diễn chuỗi.
__str__(self)
Được gọi bởi hàm dựng sẵn str() của Python và bởi lệnh print để hiển thị biểu diễn chuỗi không chính thức của một đối tượng. Lưu ý rằng nó không bắt buộc phải là một biểu thức Python hợp lệ.
__repr__(self)
Được gọi bởi hàm dựng sẵn repr() của Python và các phép chuyển đổi chuỗi để hiển thị biểu diễn chuỗi chính thức của một đối tượng. Lưu ý rằng biểu diễn này nên trông giống một biểu thức Python hợp lệ, nhưng nếu không thể, một chuỗi mô tả hữu ích vẫn được ưu tiên.
__unicode__(self)
Được gọi để triển khai hàm dựng sẵn unicode() của Python và trả về một đối tượng Unicode.
__hash__(self)
Được gọi bởi hàm dựng sẵn hash() của Python và các phép toán trên phần tử của tập hợp băm. Phương thức nên trả về một số nguyên.
__nonzero__(self)
Được gọi bởi phép toán dựng sẵn bool() của Python để kiểm tra giá trị chân lý. Phương thức nên trả về True hoặc False (hoặc tương đương số nguyên 1 hoặc 0).
Các phương thức “ma thuật” về đóng gói (Encapsulation)
Đóng gói là một trong những khái niệm nền tảng trong lập trình hướng đối tượng (OOP). Nó đề cập đến việc gom dữ liệu cùng với các phương thức thao tác trên dữ liệu đó, hoặc hạn chế truy cập trực tiếp vào một số thành phần nhất định trong một đối tượng.
Các lập trình viên đến từ những ngôn ngữ OOP khác thường tranh luận về việc Python không thực sự ôm trọn khái niệm đóng gói cho class. Họ đã quen với các bộ sửa đổi (modifier) tường minh cho method hoặc field, cho phép định nghĩa những thứ như thuộc tính private với getter và setter public. Khác biệt ở Python là phần lớn cơ chế đóng gói sẵn có diễn ra thông qua magic methods.
Ví dụ, __setattr__ là một giải pháp đóng gói cho phép chúng ta định nghĩa hành vi khi gán giá trị cho một thuộc tính dù thuộc tính đó có tồn tại hay không. Nói cách khác, có thể định nghĩa các quy tắc cho việc thay đổi giá trị của thuộc tính.
import string
classAlphabet():def__setattr__(self, name, value):
self.__dict__[name]= value.upper()
a = Alphabet()# Assinging an attribute that does not exist# The setattr should convert it to caps despite being set as lowercase
a.alphabet = string.ascii_lowercase
print(a.alphabet)Được hỗ trợ bởi
Trong đoạn mã trên, chúng ta tạo một class tên Alphabet không có thuộc tính nào, nhưng chúng ta bảo Python rằng muốn viết hoa bất kỳ thuộc tính nào của instance thuộc class này khi đặt giá trị.
Chúng tôi hiểu OOP có thể hơi khó tiếp cận với một số độc giả. Để bắt kịp OOP trong Python, hãy xem Lộ trình Python Programmer của DataCamp.
Các magic methods về toán học
Một nhóm magic methods khác trong Python là các magic methods về toán học (còn gọi là các toán tử số học thông thường). Biểu thức được tạo bằng các ký hiệu đặc biệt gọi là ‘toán tử’ trong Python. Việc dùng các toán tử như vậy trong một biểu thức liên quan đến việc tạo các đối tượng có chứa magic methods về toán học. Không tuân thủ quy tắc này sẽ làm phát sinh TypeError.
classNumbers:def__init__(self, a, b):
self.a = a
self.b = b
set_a = Numbers(2,4)
set_b = Numbers(3,5)
print(set_a + set_b)"""
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_152/3639634723.py in <module>
7 set_b = Numbers(3, 5)
8
----> 9 print(set_a + set_b)
TypeError: unsupported operand type(s) for +: 'Numbers' and 'Numbers'
"""Được hỗ trợ bởi
Trong đoạn mã trên, chúng ta thử tạo nhiều instance của class Numbers và cộng chúng lại, dẫn đến lỗi như dự kiến. Một cách giải quyết là tạo một method trong class để cộng các số lại, nhưng cách này sẽ ngăn chúng ta tạo biểu thức.
Một giải pháp tốt hơn là triển khai magic method __add__ như sau:
# Using add operatorclassNumbers:def__init__(self, a, b):
self.a = a
self.b = b
def__add__(self, other):# Only permit Numbers objects to be addedifnotisinstance(other, Numbers):return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)def__repr__(self):returnf"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2,4)
b = Numbers(3,5)print(a + b)# Should result in Numbers(5, 9)"""
RandomNumbers(5, 9)
"""Được hỗ trợ bởi
Ví dụ mã trên chỉ cho phép cộng hai đối tượng Numbers với nhau. Hãy triển khai thêm một magic method về toán học để cho phép nhân một đối tượng Numbers với một số nguyên.
# Multiplying Numbers with an integerclassNumbers:def__init__(self, a, b):
self.a = a
self.b = b
def__add__(self, other):# Only permit Numbers objects to be addedifnotisinstance(other, Numbers):return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)def__mul__(self, other):ifnotisinstance(other,int):return NotImplemented
return Numbers(self.a * other, self.b * other)def__repr__(self):returnf"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2,4)print(a *5)# Should return 10, 20"""
RandomNumbers(10, 20)
"""Được hỗ trợ bởi
Trong đoạn mã trên, chúng ta thêm magic method __mul__ để cho phép nhân các số của class tùy chỉnh với một số nguyên.
Lưu ý đối tượng Numbers nằm ở phía bên trái của toán tử * – điều này không phải ngẫu nhiên. Hãy đặt nó ở bên phải toán hạng và xem điều gì xảy ra.
- snip --
a = Numbers(2,4)print(3* a)---------------------------------------------------------------------------
TypeError Traceback (most recent call last)/tmp/ipykernel_152/2857655049.py in<module>1 a = Numbers(2,4)---->2print(3* a)
TypeError: unsupported operand type(s)for*:'int'and'Numbers'Được hỗ trợ bởi
Python ném ra TypeError.
Magic method __mul__ chúng ta triển khai chỉ xử lý trường hợp đối tượng Numbers ở phía bên trái toán tử *. Để mở rộng chức năng này, chúng ta phải thêm magic method đảo ngược, __rmul__.
# Enabling reverse multiplicationclassNumbers:def__init__(self, a, b):
self.a = a
self.b = b
def__add__(self, other):# Only permit Numbers objects to be addedifnotisinstance(other, Numbers):return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)def__mul__(self, other):ifnotisinstance(other,int):return NotImplemented
return Numbers(self.a * other, self.b * other)def__rmul__(self, other):return self.__mul__(other)def__repr__(self):returnf"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2,4)print(5* a)# Should return 10, 20"""
Numbers(10, 20)
"""Được hỗ trợ bởi
Giờ đây, chúng ta có thể tạo biểu thức nhân mà không cần bận tâm đối tượng Numbers nằm ở phía nào của toán hạng.
Tổng kết
Nguyên tắc cơ bản để tận dụng magic methods trong Python vẫn giữ nguyên: tạo một class và ghi đè magic method của class object. Có rất nhiều magic methods trong Python, nghĩa là hướng dẫn sẽ cực kỳ dài nếu chúng ta quyết định đề cập từng cái một. Chúng tôi khuyến nghị bạn xem tài liệu Python để có cái nhìn tổng quan về toàn bộ phạm vi các magic methods bạn có thể dùng để nâng cao kỹ năng lập trình Python.
Một trong những yếu tố góp phần khiến Python trở thành ngôn ngữ lập trình mạnh mẽ là các magic methods. Trong một số cộng đồng Pythonista, bạn có thể nghe chúng được gọi là ‘dunder methods’ do cấu trúc hai dấu gạch dưới.
Vai trò của chúng trong Python là thực hiện một quy trình gọi là ‘quá tải toán tử’ (operator overloading), nghĩa là một toán tử được định nghĩa sẵn được gán thêm ý nghĩa mở rộng. Nói cách khác, operator overloading được dùng để bổ sung hành vi tùy chỉnh cho các lớp (class) của chúng ta nhằm cho phép chúng hoạt động với các toán tử và chức năng dựng sẵn của Python.
Trong bài viết này, chúng ta sẽ đi sâu hơn vào magic methods trong Python bằng cách:
Xem sổ tay DataLab này để làm theo đoạn mã dùng trong hướng dẫn.
Phương thức (Method) và Hàm (Function) trong Python
Trước khi tìm hiểu magic methods là gì, chúng ta cần hiểu sự khác nhau giữa method và function trong Python.
Python dùng method để biểu diễn hành vi của một đối tượng (object). Vì vậy, method gắn với một đối tượng và sẽ thực hiện một số thao tác trên dữ liệu bên trong đối tượng đó. Một yếu tố phân biệt quan trọng khác của method là tham số self phải luôn xuất hiện như tham số đầu tiên được truyền vào method.
Lưu ý: Tham số đầu tiên không bắt buộc phải đặt tên là self, nhưng cộng đồng Pythonista thống nhất đây là thực hành tốt nhất.
Các method phụ thuộc vào class mà chúng gắn với. Do đó, để gọi một method, cần tạo một thể hiện (instance) của class trước khi có thể truy cập method bằng toán tử chấm.
Một function thì khác: chúng ta không phải tạo instance của class.
Hàm trong Python là các bước được thực thi như một khối mã chỉ khi được gọi. Chúng được định nghĩa độc lập với bất kỳ đối tượng class nào, nghĩa là có thể gọi trực tiếp bằng tên. Vì không gắn với class nào, chúng ta không cần truyền self làm tham số – các tham số là tùy chọn.
Tóm lại, function đơn giản là một tập hợp các chỉ dẫn được xác định rõ ràng mà trình thông dịch dùng để thực hiện một tác vụ.
Như đã trình bày trong bài Cách gọi và viết hàm, function cũng có hai dạng: dựng sẵn và do người dùng định nghĩa.
Ở nhiều khía cạnh, method và function giống nhau – ví dụ, cả hai đều được định nghĩa bằng câu lệnh def. Điểm khác biệt cốt lõi là method gắn với một đối tượng và dùng để thao tác trên dữ liệu nằm trong một class, trong khi function được định nghĩa độc lập và có thể được gọi bằng tên ở bất cứ đâu trong chương trình.
Python magic methods là gì?
Ở phần đầu hướng dẫn, chúng tôi đề cập rằng magic methods còn được gọi là dunder methods do cấu trúc hai dấu gạch dưới. Ý chúng tôi là tất cả magic methods trong Python đều bắt đầu và kết thúc bằng hai dấu gạch dưới.
Magic method nổi tiếng nhất là phương thức __init__, cho phép khởi tạo một class với các thuộc tính cụ thể.
Để tạo một instance của class này, chúng ta phải chỉ định các thuộc tính name, age và role của instance. Ví dụ:
Nếu không, sẽ phát sinh TypeError.
Phương thức __init__ thuộc nhóm magic methods khởi tạo và dựng (initialization and construction), nhưng nó chỉ là một trong số nhiều phương thức. Bảng dưới đây nêu một số magic methods khác thuộc nhóm khởi tạo và dựng.
__new__(cls, other)
Dùng để tạo một instance mới của class
__init__(self, other)
Được gọi sau khi một instance mới được tạo; Tất cả thuộc tính truyền vào biểu thức khởi tạo class sẽ được dùng làm thuộc tính.
__del__(self)
Còn gọi là destructor; Được gọi khi một instance sắp bị hủy.
Cách đơn giản nhất để hình dung magic methods là một bản hợp đồng giữa phần triển khai của bạn và trình thông dịch Python. Một trong các điều khoản yêu cầu Python thực hiện một số hành động ngầm khi có những điều kiện nhất định, chẳng hạn như khởi tạo một instance của class với các đối tượng cụ thể.
Nói cách khác, bạn không có trách nhiệm gọi trực tiếp magic method – Python sẽ làm điều đó cho bạn ở hậu trường.
Các phương thức biểu diễn chuỗi
Python là một ngôn ngữ lập trình hướng đối tượng (OOP) – mọi thứ trong Python đều là đối tượng. Khi một đối tượng mới được tạo, chúng ta ngầm tạo ra một đối tượng liên quan vì mọi class đều kế thừa từ object.
Lưu ý: bạn cũng có thể làm cho việc kế thừa trở nên tường minh bằng cách truyền class object vào class của bạn.
Trong đoạn mã trên, chúng ta tạo hai class rỗng, nhưng trình thông dịch Python vẫn biết in gì khi gọi lệnh print. Điều này khả thi vì mọi class đều kế thừa từ class object (lớp cha của mọi class trong Python), và nó có một magic method tên là __repr__, mà lệnh print sẽ gọi để trả về một giá trị cho chương trình chính của chúng ta.
Những ai quen thuộc với kế thừa sẽ biết rằng một lớp con có thể ghi đè lớp cha bằng cách định nghĩa method cùng tên của riêng mình.
Bạn cũng có thể dùng magic method __str__ để tạo biểu diễn chuỗi được gọi bằng lệnh print.
Thực tế, magic method __repr__ đóng vai trò dự phòng cho magic method __str__. Vì vậy, khi chúng ta gọi lệnh print, Python sẽ tìm __str__ được định nghĩa trong class của bạn trước, rồi mới quay về __repr__ nếu không có.
Bảng dưới đây liệt kê một số magic methods khác liên quan đến biểu diễn chuỗi.
__str__(self)
Được gọi bởi hàm dựng sẵn str() của Python và bởi lệnh print để hiển thị biểu diễn chuỗi không chính thức của một đối tượng. Lưu ý rằng nó không bắt buộc phải là một biểu thức Python hợp lệ.
__repr__(self)
Được gọi bởi hàm dựng sẵn repr() của Python và các phép chuyển đổi chuỗi để hiển thị biểu diễn chuỗi chính thức của một đối tượng. Lưu ý rằng biểu diễn này nên trông giống một biểu thức Python hợp lệ, nhưng nếu không thể, một chuỗi mô tả hữu ích vẫn được ưu tiên.
__unicode__(self)
Được gọi để triển khai hàm dựng sẵn unicode() của Python và trả về một đối tượng Unicode.
__hash__(self)
Được gọi bởi hàm dựng sẵn hash() của Python và các phép toán trên phần tử của tập hợp băm. Phương thức nên trả về một số nguyên.
__nonzero__(self)
Được gọi bởi phép toán dựng sẵn bool() của Python để kiểm tra giá trị chân lý. Phương thức nên trả về True hoặc False (hoặc tương đương số nguyên 1 hoặc 0).
Các phương thức “ma thuật” về đóng gói (Encapsulation)
Đóng gói là một trong những khái niệm nền tảng trong lập trình hướng đối tượng (OOP). Nó đề cập đến việc gom dữ liệu cùng với các phương thức thao tác trên dữ liệu đó, hoặc hạn chế truy cập trực tiếp vào một số thành phần nhất định trong một đối tượng.
Các lập trình viên đến từ những ngôn ngữ OOP khác thường tranh luận về việc Python không thực sự ôm trọn khái niệm đóng gói cho class. Họ đã quen với các bộ sửa đổi (modifier) tường minh cho method hoặc field, cho phép định nghĩa những thứ như thuộc tính private với getter và setter public. Khác biệt ở Python là phần lớn cơ chế đóng gói sẵn có diễn ra thông qua magic methods.
Ví dụ, __setattr__ là một giải pháp đóng gói cho phép chúng ta định nghĩa hành vi khi gán giá trị cho một thuộc tính dù thuộc tính đó có tồn tại hay không. Nói cách khác, có thể định nghĩa các quy tắc cho việc thay đổi giá trị của thuộc tính.
Trong đoạn mã trên, chúng ta tạo một class tên Alphabet không có thuộc tính nào, nhưng chúng ta bảo Python rằng muốn viết hoa bất kỳ thuộc tính nào của instance thuộc class này khi đặt giá trị.
Chúng tôi hiểu OOP có thể hơi khó tiếp cận với một số độc giả. Để bắt kịp OOP trong Python, hãy xem Lộ trình Python Programmer của DataCamp.
Các magic methods về toán học
Một nhóm magic methods khác trong Python là các magic methods về toán học (còn gọi là các toán tử số học thông thường). Biểu thức được tạo bằng các ký hiệu đặc biệt gọi là ‘toán tử’ trong Python. Việc dùng các toán tử như vậy trong một biểu thức liên quan đến việc tạo các đối tượng có chứa magic methods về toán học. Không tuân thủ quy tắc này sẽ làm phát sinh TypeError.
Trong đoạn mã trên, chúng ta thử tạo nhiều instance của class Numbers và cộng chúng lại, dẫn đến lỗi như dự kiến. Một cách giải quyết là tạo một method trong class để cộng các số lại, nhưng cách này sẽ ngăn chúng ta tạo biểu thức.
Một giải pháp tốt hơn là triển khai magic method __add__ như sau:
Ví dụ mã trên chỉ cho phép cộng hai đối tượng Numbers với nhau. Hãy triển khai thêm một magic method về toán học để cho phép nhân một đối tượng Numbers với một số nguyên.
Trong đoạn mã trên, chúng ta thêm magic method __mul__ để cho phép nhân các số của class tùy chỉnh với một số nguyên.
Lưu ý đối tượng Numbers nằm ở phía bên trái của toán tử * – điều này không phải ngẫu nhiên. Hãy đặt nó ở bên phải toán hạng và xem điều gì xảy ra.
Python ném ra TypeError.
Magic method __mul__ chúng ta triển khai chỉ xử lý trường hợp đối tượng Numbers ở phía bên trái toán tử *. Để mở rộng chức năng này, chúng ta phải thêm magic method đảo ngược, __rmul__.
Giờ đây, chúng ta có thể tạo biểu thức nhân mà không cần bận tâm đối tượng Numbers nằm ở phía nào của toán hạng.
Tổng kết
Nguyên tắc cơ bản để tận dụng magic methods trong Python vẫn giữ nguyên: tạo một class và ghi đè magic method của class object. Có rất nhiều magic methods trong Python, nghĩa là hướng dẫn sẽ cực kỳ dài nếu chúng ta quyết định đề cập từng cái một. Chúng tôi khuyến nghị bạn xem tài liệu Python để có cái nhìn tổng quan về toàn bộ phạm vi các magic methods bạn có thể dùng để nâng cao kỹ năng lập trình Python.