Nạp chồng toán tử trong Python

Bạn có thể thay đổi ý nghĩa của toán tử trong Python tùy thuộc vào toán hạng được sử dụng và ta gọi đó là nạp chồng toán tử.

Nạp chồng toán tử trong Python là gì?

Toán tử Python làm việc bằng các hàm được dựng sẵn, nhưng một toán tử có thể được sử dụng để thực hiện nhiều hoạt động khác nhau. Ví dụ với toán tử ' + ', bạn có thể cộng số học hai số với nhau, có thể kết hợp hai danh sách, hoặc nối hai chuỗi khác nhau lại…

Tính năng này trong Python gọi là nạp chồng toán tử, cho phép cùng một toán tử được sử dụng khác nhau tùy từng ngữ cảnh.

Vậy vấn đề gì sẽ xảy ra khi ta sử dụng nạp chồng toán tử với object của một lớp do người dùng khai báo? Hãy theo dõi ví dụ mô phỏng một điểm trong hệ tọa độ hai chiều sau:

class Point:
  def __init__(self, x = 0, y = 0):
  self.x = x
  self.y = y

Ta chạy chương trình và nhập vào các điểm:

>>> p1 = Point(2,3)
>>> p2 = Point(-1,2)
>>> p1 + p2
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Point' and 'Point'

Chương trình ngay lập tức báo lỗi TypeError vì Python không thể nhận hai đối tượng Point cùng lúc.

Để xử lý vấn đề này, ta sẽ sử dụng nạp chồng toán tử.

Trước tiên hãy tìm hiểu qua một số hàm đặc biệt sau đây.

Các hàm đặc biệt trong Python

Hàm trong Class được bắt đầu với hai dấu gạch dưới liền nhau (__) là các hàm đặc biệt, mang các ý nghĩa đặc biệt.

Có rất nhiều hàm đặc biệt trong Python và một trong đó là hàm __init__() mà Quantrimang đã giới thiệu trước đó trong bài học về Class và Object. Hàm này được gọi bất cứ khi nào khởi tạo một đối tượng, một biến mới trong class.

Mục đích khi sử dụng các hàm đặc biệt này là giúp những hàm của chúng ta tương thích với các hàm được dựng sẵn trong Python.

>>> p1 = Point(2,3)
>>> print(p1)
<__main__.Point object at 0x00000000031F8CC0>

Bạn nên khai báo phương thức __str__() trong class để kiểm soát cách hiển thị kết quả được in ra.

class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y

def __str__(self):
return "({0},{1})".format(self.x,self.y)

Và bây giờ thử lại với hàm print()

>>> p1 = Point(2,3)
>>> print(p1)
(2,3)

Sử dụng __str__() làm kết quả hiển thị chuẩn hơn. Ngoài ra bạn có thể in ra kết quả tương tự bằng cách sử dụng hàm tích hợp sẵn trong Python là str() hoặc format().

>>> str(p1)
'(2,3)'

>>> format(p1)
'(2,3)'

Khi sử dụng str() và format(), Python thực hiện lệnh gọi p1.__str__() nên kết quả được trả về tương tự.

Nạp chồng toán tử ' + ' trong Python

Để nạp chồng toán tử ' + ', ta sẽ sử dụng hàm __add__() trong class. Ta có thể triển khai nhiều công việc bằng hàm này, ví dụ như cộng hai điểm tọa độ ở ví dụ bên trên.

class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y

def __str__(self):
return "({0},{1})".format(self.x,self.y)

def __add__(self,other):
x = self.x + other.x
y = self.y + other.y
return Point(x,y)

Ta chạy chương trình và nhập vào các điểm:

>>> p1 = Point(2,3)
>>> p2 = Point(-1,2)
>>> print(p1 + p2)
(1,5)

Ở chương trình này, khi bạn thực hiện p1 + p2, Python sẽ gọi ra p1.__add__(p2).

Tương tự như vậy, bạn có thể nạp chồng nhiều toán tử khác. Quantrimang xin giới thiệu một số hàm đặc biệt dùng cho nạp chồng toán tử trong bảng dưới đây:

TOÁN TỬ BIỂU DIỄN HOẠT ĐỘNG
Phép cộng p1 + p2 p1.__add__(p2)
Phép trừ p1 - p2 p1.__sub__(p2)
Phép nhân p1 * p2 p1.__mul__(p2)
Lũy thừa p1 ** p2 p1.__pow__(p2)
Phép chia p1 / p2 p1.__truediv__(p2)
Phép chia lấy phần nguyên (Floor Division) p1 // p2 p1.__floordiv__(p2)
Số dư (modulo) p1 % p2 p1.__mod__(p2)
Thao tác trên bit: phép dịch trái p1 << p2 p1.__lshift__(p2)
Thao tác trên bit: phép dịch phải p1 >> p2 p1.__rshift__(p2)
Thao tác trên bit: phép AND p1 & p2 p1.__and__(p2)
Thao tác trên bit: phép OR p1 | p2 p1.__or__(p2)
Thao tác trên bit: phép XOR p1 ^ p2 p1.__xor__(p2)
Thao tác trên bit: phép NOT ~p1 p1.__invert__()

Nạp chồng toán tử so sánh trong Python

Python không chỉ giới hạn được phép nạp chồng các toán tử toán học, mà còn cho phép người dùng nạp chồng toán tử so sánh.

Có nhiều toán tử so sánh được hỗ trợ bởi Python, ví dụ như: <, >, <=, >=, ==,...

Bạn sử dụng nạp chồng toán tử này khi muốn so sánh các đối tượng trong lớp với nhau.

Ví dụ bạn muốn so sánh các điểm trong class Point, hãy so sánh độ lớn của các điểm này bắt đầu từ gốc tọa độ, thực hiện như sau:

class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y

def __str__(self):
return "({0},{1})".format(self.x,self.y)

def __lt__(self,other):
self_mag = (self.x ** 2) + (self.y ** 2)
other_mag = (other.x ** 2) + (other.y ** 2)
return self_mag < other_mag

Ta chạy chương trình và nhập vào các điểm và toán tử dùng để só sánh:

>>> Point(1,1) < Point(-2,-3)
True

>>> Point(1,1) < Point(0.5,-0.2)
False

>>> Point(1,1) < Point(1,1)
False

Tương tự như vậy, bạn có thể nạp chồng nhiều toán tử so sánh khác. Quantrimang xin giới thiệu một số hàm đặc biệt dùng cho nạp chồng toán tử so sánh trong bảng dưới đây:

TOÁN TỬ BIỂU DIỄN HOẠT ĐỘNG
Nhỏ hơn p1 < p2 p1.__lt__(p2)
Nhỏ hơn hoặc bằng p1 <= p2 p1.__le__(p2)
Bằng p1 == p2
p1.__eq__(p2)
Khác p1 != p2 p1.__ne__(p2)
Lớn hơn p1 > p2 p1.__gt__(p2)
Lớn hơn hoặc bằng p1 >= p2 p1.__ge__(p2)

Xem thêm:

Bài trước: Đa kế thừa (Multiple Inheritance) trong Python

Thứ Năm, 13/12/2018 10:32
53 👨 351