CodeGym /Các khóa học /Python SELF VI /Nạp chồng toán tử

Nạp chồng toán tử

Python SELF VI
Mức độ , Bài học
Có sẵn

6.1 Phương thức ma thuật

Nạp chồng toán tử trong Python cho phép xác định hoặc thay đổi hành vi của các toán tử tích hợp sẵn (ví dụ: +, -, *, /) cho các lớp tự tạo. Điều này được thực hiện thông qua các phương thức đặc biệt, còn được gọi là phương thức ma thuật.

Ví dụ, trong lớp của mình, bạn có thể nạp chồng các toán tử so sánh:

Toán tử Phương thức không gạch dưới Chữ ký phương thức
== eq() __eq__(self, other)
!= ne() __ne__(self, other)
< lt() __lt__(self, other)
<= le() __le__(self, other)
> gt() __gt__(self, other)
>= ge() __ge__(self, other)

Giả sử, bạn đã viết lớp của mình và muốn các đối tượng của lớp đó được so sánh theo cách bạn muốn. Bạn chỉ cần thực hiện phương thức «__eq__» trong lớp của mình, và Python sẽ gọi phương thức này mỗi khi đối tượng của bạn được so sánh trong mã.

Ví dụ:


class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
        
# Sử dụng
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2)  # Sẽ in ra: True
print(v1 == v3)  # Sẽ in ra: False

Mỗi lần bạn so sánh hai đối tượng của bạn, Python sẽ kiểm tra xem có phương thức hàm «__eq__» nào được thực hiện không. Nếu có, sẽ gọi nó. Còn nếu không, sẽ chỉ so sánh liên kết của đối tượng.

Thực tế, ví dụ trên đã viết (chỉ cần thêm kiểm tra có phương thức):


# Sử dụng
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2))  # Sẽ in ra: True
print(v1.__eq__(v3))  # Sẽ in ra: False

6.2 Danh sách tất cả các toán tử

Tổng cộng có 6 nhóm toán tử sẵn sàng cho việc nạp chồng.

Toán tử số học:

Toán tử Phương thức không gạch dưới Chữ ký phương thức
+ add __add__(self, other)
- sub __sub__(self, other)
* mul __mul__(self, other)
/ truediv __truediv__(self, other)
// floordiv __floordiv__(self, other)
% mod __mod__(self, other)
** pow __pow__(self, other)

Toán tử so sánh:

Toán tử Phương thức không gạch dưới Chữ ký phương thức
== eq __eq__(self, other)
!= ne __ne__(self, other)
< lt __lt__(self, other)
<= le __le__(self, other)
> gt __gt__(self, other)
>= ge __ge__(self, other)

Toán tử logic:

Toán tử Phương thức không gạch dưới Chữ ký phương thức
& and __and__(self, other)
| or __or__(self, other)
^ xor __xor__(self, other)
~ invert __invert__(self)

Toán tử chỉ mục và cắt:

Toán tử Phương thức
obj[key] __getitem__(self, key)
obj[key] = value __setitem__(self, key, value)
del obj[key] __delitem__(self, key)

Toán tử đơn:

Toán tử Phương thức
- __neg__(self)
+ __pos__(self)
abs() __abs__(self)
~ __invert__(self)

Toán tử gán:

Toán tử Phương thức
+= __iadd__(self, other)
-= __isub__(self, other)
*= __imul__(self, other)
/= __itruediv__(self, other)
//= __ifloordiv__(self, other)
%= __imod__(self, other)
**= __ipow__(self, other)

Có lẽ vì vậy mà Python có vẻ chậm - mỗi lần trước khi thực hiện một toán tử, Python tìm kiếm một hàm tương tự trong lớp và tất cả lớp cha của nó. Nhưng điều đó cho phép viết mã súc tích nhất trên thế giới :)

6.3 Toán tử chỉ mục

Việc có thể so sánh các đối tượng hay trừ các tập hợp - trong một số tình huống có thể là rõ ràng. Và bạn tự nhận ra điều đó, nếu bạn viết lớp mà ngụ ý rằng nó cần có các phép toán logic hay toán học.

Mình muốn cùng các bạn thảo luận một ví dụ thú vị thế này – đó là toán tử chỉ mục. Hãy bắt đầu ngay với mã ví dụ:


class CustomList:
    def __init__(self, data):
        self.data = data
        
    def __getitem__(self, index):
        return self.data[index]
        
    def __setitem__(self, index, value):
        self.data[index] = value
        
    def __delitem__(self, index):
        del self.data[index]
        
    def __repr__(self):
        return repr(self.data)
        
# Sử dụng
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1])  # Sẽ in ra: 2
c_list[1] = 10
print(c_list)  # Sẽ in ra: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list)  # Sẽ in ra: [1, 3, 4, 5]

Mình thấy trong ví dụ này có ba phép toán:

  • Đọc dữ liệu thông qua chỉ mục
  • Ghi dữ liệu thông qua chỉ mục
  • Và thậm chí là xóa dữ liệu thông qua chỉ mục.

Mà thực ra không cần phải lưu trữ dữ liệu dưới dạng danh sách. Hoặc chỉ mục không nhất thiết phải là một số. Ví dụ, lớp dictionary (từ điển) được thực hiện như vậy.

Tạo SuperList

Hãy nhớ lớp list? Bạn có thể gán phần tử, nhưng chỉ với các chỉ mục có sẵn. Hãy tạo lớp của mình, đặt tên là SuperList, mà phần tử có thể được truy cập bằng bất kỳ chỉ mục nào:

  • Nếu chỉ mục < 0, thì chèn phần tử vào đầu
  • Nếu chỉ mục >= len, thì thêm phần tử vào cuối
  • Trong các trường hợp còn lại chỉ cần trả về phần tử

Ví dụ:


class SuperList(list):
    def __init__(self, value):
        super().__init__(value)
        
    def __setitem__(self, index, value):
        if index >= len(self):
            super().append(value)
        elif index < 0:
            super().insert(0, value)
        else:
            super().__setitem__(index, value)
        
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst)  # [99, 1, 2, 3, 100]

Vậy nên nạp chồng chỉ mục thực sự là một cơ hội tuyệt vời, và mình khuyên bạn nên sử dụng nó trong thực tế. Còn hôm nay – hết rồi nha.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION