INTRODUCTION
FLOW CONTROL
FUNCTIONS
DATATYPES
OBJECT & CLASS
Bài tập Python: Lập trình hướng đối tượng (OOP) trong Python Lập trình hướng đối tượng trong Python Class Variables trong Python Tìm hiểu về Methods trong Python Cách sử dụng phương thức __init__() trong Python Các biến Instance trong Python Tìm hiểu về Class Attributes trong Python Hàm Static Methods trong Python Phương thức __str__ trong Python Phương thức __repr__ trong Python Phương thức eq trong Python Tìm hiểu phương thức __hash__ trong Python Phương thức __bool__ trong Python Phương thức del trong Python Tìm hiểu về lớp Property trong Python Tìm hiểu về nạp chồng toán tử trong Python Trình Decorator Property trong Python Thuộc tính chỉ đọc trong Python Thuộc tính Delete trong Python Sử dụng super() trong Python Sử dụng __slots__ trong Python Cách sử dụng Protocol trong Python Sử dụng Enum aliases và @enum.unique trong Python Tùy chỉnh và mở rộng lớp Enum trong Python Cách sử dụng hàm Auto() của Python Single Responsibility Principle trong Python Nguyên tắc Đóng-Mở trong Python Nguyên tắc thay thế Liskov - LSP trong Python Interface Segregation Principle - ISP trong Python. Nguyên tắc đảo ngược sự phụ thuộc trong Python Đa kế thừa trong Python Tìm hiểu về các lớp mixin trong Python Mô tả Descriptors trong Python Phân biệt Data Descriptor và Non-data Descriptor trong Python Phương thức __new__ trong Python Tìm hiểu về Class Type trong Python Lớp Metaclass trong Python Ví dụ sử dụng metaclass trong Python Tìm hiểu về decorator dataclass trong Python Tìm hiểu về các ngoại lệ trong Python Ngoại lệ Raise trong Python Sử dụng câu lệnh raise from trong Python Ngoại lệ tùy chỉnh trong Python Module trong Python Package trong Python Class trong Python Hàm khởi tạo trong Python Kế thừa trong Python Đa kế thừa trong Python Setter và Getter trong Python Override trong Python Interface trong Python Bài tập Python: Module và Class
ADVANCED TOPICS
BỔ SUNG
PYTHON CĂN BẢN
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Tùy chỉnh và mở rộng lớp Enum trong Python

Trong bài viết này, mình sẽ tìm hiểu cách tùy chỉnh và mở rộng các lớp Enum (liệt kê) trong Python. Python Enum không chỉ đơn thuần là một danh sách các hằng số, mà chúng còn là những lớp có thể được mở rộng và tùy chỉnh với các phương thức đặc biệt (dunder methods) để tạo ra những hành vi riêng biệt. Qua việc triển khai các phương thức như __str__, __eq__, và __lt__, bạn có thể thay đổi cách các giá trị Enum được so sánh, sắp xếp hoặc hiển thị, giúp tăng cường tính linh hoạt và sức mạnh của các lớp liệt kê trong Python.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Tùy chỉnh lớp Enum trong Python

Các liệt kê (enumerations) trong Python thực chất là các lớp (classes). Điều này có nghĩa là bạn có thể thêm các phương thức (methods) vào chúng hoặc triển khai các phương thức đặc biệt (dunder methods) để tùy chỉnh hành vi của chúng.

Ví dụ sau định nghĩa lớp liệt kê PaymentStatus:

from enum import Enum

class PaymentStatus(Enum):
    PENDING = 1
    COMPLETED = 2
    REFUNDED = 3

Lớp PaymentStatus có ba thành viên: PENDING, COMPLETED, và REFUNDED.

Bài viết này được đăng tại [free tuts .net]

Nếu bạn hiển thị thành viên PENDING:

print(PaymentStatus.PENDING)

Kết quả sẽ là:

PaymentStatus.PENDING

Tùy chỉnh hiển thị chuỗi

Bạn có thể tùy chỉnh cách hiển thị chuỗi của các thành viên trong liệt kê bằng cách triển khai phương thức __str__. Ví dụ:

from enum import Enum

class PaymentStatus(Enum):
    PENDING = 1
    COMPLETED = 2
    REFUNDED = 3

    def __str__(self):
        return f'{self.name.lower()}({self.value})'

print(PaymentStatus.PENDING)

Kết quả sẽ là:

pending(1)

Triển khai phương thức __eq__

Khi so sánh thành viên của PaymentStatus với một số nguyên, ví dụ như:

if PaymentStatus.PENDING == 1:
    print('The payment is pending.')

Sẽ không có gì được in ra, vì PaymentStatus.PENDING không bằng với số nguyên 1.

Để so sánh các thành viên của PaymentStatus với số nguyên, bạn có thể triển khai phương thức __eq__ như sau:

from enum import Enum

class PaymentStatus(Enum):
    PENDING = 1
    COMPLETED = 2
    REFUNDED = 3

    def __str__(self):
        return f'{self.name.lower()}({self.value})'

    def __eq__(self, other):
        if isinstance(other, int):
            return self.value == other
        if isinstance(other, PaymentStatus):
            return self is other
        return False

if PaymentStatus.PENDING == 1:
    print('The payment is pending.')

Trong phương thức __eq__:

  • Nếu giá trị so sánh là số nguyên, nó sẽ so sánh giá trị của thành viên với số nguyên.
  • Nếu giá trị so sánh là một thể hiện của PaymentStatus, nó sẽ so sánh giá trị với thành viên bằng toán tử is.
  • Nếu không, nó sẽ trả về False.

Kết quả của đoạn mã trên sẽ là:

The payment is pending.

Triển khai phương thức __lt__

Giả sử bạn muốn các thành viên của PaymentStatus có thứ tự sắp xếp dựa trên giá trị của chúng, và bạn cũng muốn so sánh chúng với một số nguyên.

Bạn có thể triển khai phương thức __lt__ và sử dụng decorator @total_ordering từ module functools:

from enum import Enum
from functools import total_ordering

@total_ordering
class PaymentStatus(Enum):
    PENDING = 1
    COMPLETED = 2
    REFUNDED = 3

    def __str__(self):
        return f'{self.name.lower()}({self.value})'

    def __eq__(self, other):
        if isinstance(other, int):
            return self.value == other
        if isinstance(other, PaymentStatus):
            return self is other
        return False

    def __lt__(self, other):
        if isinstance(other, int):
            return self.value < other
        if isinstance(other, PaymentStatus):
            return self.value < other.value
        return False

# So sánh với một số nguyên
status = 1
if status < PaymentStatus.COMPLETED:
    print('The payment has not completed.')

# So sánh với một thành viên khác
status = PaymentStatus.PENDING
if status >= PaymentStatus.COMPLETED:
    print('The payment is not pending.')

Triển khai phương thức __bool__

Mặc định, tất cả các thành viên của liệt kê đều có giá trị là True. Ví dụ:

for member in PaymentStatus:
    print(member, bool(member))

Nếu bạn muốn thay đổi hành vi này, bạn có thể triển khai phương thức __bool__. Giả sử bạn muốn các thành viên COMPLETEDREFUNDED có giá trị True, còn PENDINGFalse. Đoạn mã sau minh họa cách làm điều này:

from enum import Enum
from functools import total_ordering

@total_ordering
class PaymentStatus(Enum):
    PENDING = 1
    COMPLETED = 2
    REFUNDED = 3

    def __str__(self):
        return f'{self.name.lower()}({self.value})'

    def __eq__(self, other):
        if isinstance(other, int):
            return self.value == other
        if isinstance(other, PaymentStatus):
            return self is other
        return False

    def __lt__(self, other):
        if isinstance(other, int):
            return self.value < other
        if isinstance(other, PaymentStatus):
            return self.value < other.value
        return False

    def __bool__(self):
        if self is self.COMPLETED:
            return True
        return False

for member in PaymentStatus:
    print(member, bool(member))

Kết quả sẽ là:

pending(1) False
completed(2) True
refunded(3) False

Mở rộng lớp Enum trong Python

Python không cho phép mở rộng một lớp Enum nếu nó đã có các thành viên. Tuy nhiên, điều này không phải là một hạn chế thực sự. Bạn có thể định nghĩa một lớp cơ bản (base class) có phương thức nhưng không có thành viên, sau đó mở rộng lớp cơ bản này.

Ví dụ, đầu tiên, định nghĩa lớp cơ bản OrderedEnum để sắp xếp các thành viên theo giá trị:

from enum import Enum
from functools import total_ordering

@total_ordering
class OrderedEnum(Enum):
    def __lt__(self, other):
        if isinstance(other, OrderedEnum):
            return self.value < other.value
        return NotImplemented

Sau đó, định nghĩa lớp ApprovalStatus kế thừa từ OrderedEnum:

class ApprovalStatus(OrderedEnum):
    PENDING = 1
    IN_PROGRESS = 2
    APPROVED = 3

Cuối cùng, bạn có thể so sánh các thành viên của lớp ApprovalStatus:

status = ApprovalStatus(2)
if status < ApprovalStatus.APPROVED:
    print('The request has not been approved.')

Kết quả sẽ là:

The request has not been approved.

Kết bài

Việc triển khai các phương thức đặc biệt cho phép chúng ta tùy chỉnh hành vi của các lớp Enum trong Python, từ đó có thể dễ dàng quản lý và thao tác với các thành viên trong lớp liệt kê theo cách phù hợp với yêu cầu cụ thể của dự án. Ngoài ra, bằng cách định nghĩa một lớp Enum cơ bản không có thành viên và phương thức, chúng ta có thể mở rộng lớp này để tạo ra nhiều lớp Enum khác nhau, tận dụng lại các phương thức tùy chỉnh đã được định nghĩa. Cách tiếp cận này giúp mã nguồn trở nên linh hoạt, dễ bảo trì và tiết kiệm thời gian khi làm việc với các Enum có yêu cầu phức tạp.

Cùng chuyên mục:

Hướng dẫn xây dựng Command-Line Interface (CLI) bằng Quo trong Python

Hướng dẫn xây dựng Command-Line Interface (CLI) bằng Quo trong Python

Hướng dẫn toàn diện về module datetime trong Python

Hướng dẫn toàn diện về module datetime trong Python

Cách truy cập và thiết lập biến môi trường trong Python

Cách truy cập và thiết lập biến môi trường trong Python

Lớp dữ liệu (Data Classes) trong Python với decorator @dataclass

Lớp dữ liệu (Data Classes) trong Python với decorator @dataclass

Từ khóa yield trong Python

Từ khóa yield trong Python

Sự khác biệt giữa sort() và sorted() trong Python

Sự khác biệt giữa sort() và sorted() trong Python

Sử dụng Poetry để quản lý dependencies trong Python

Sử dụng Poetry để quản lý dependencies trong Python

Định dạng chuỗi Strings trong Python

Định dạng chuỗi Strings trong Python

Một tác vụ phổ biến khi làm việc với danh sách trong Python

Một tác vụ phổ biến khi làm việc với danh sách trong Python

Làm việc với các biến môi trường trong Python

Làm việc với các biến môi trường trong Python

Sự khác biệt giữa set() và frozenset() trong Python

Sự khác biệt giữa set() và frozenset() trong Python

Sự khác biệt giữa iterator và iterable trong Python

Sự khác biệt giữa iterator và iterable trong Python

Cách làm việc với file tarball/tar trong Python

Cách làm việc với file tarball/tar trong Python

Chuyển đổi kiểu dữ liệu trong Python

Chuyển đổi kiểu dữ liệu trong Python

Sự khác biệt giữa toán tử == và is trong Python

Sự khác biệt giữa toán tử == và is trong Python

Làm việc với file ZIP trong Python

Làm việc với file ZIP trong Python

Cách sử dụng ThreadPoolExecutor trong Python

Cách sử dụng ThreadPoolExecutor trong Python

Sự khác biệt giữa byte objects và string trong Python

Sự khác biệt giữa byte objects và string trong Python

Xử lý độ chính xác các hàm floor, ceil, round, trunc, format  trong Python

Xử lý độ chính xác các hàm floor, ceil, round, trunc, format trong Python

Cách lặp qua nhiều list với hàm zip() trong Python

Cách lặp qua nhiều list với hàm zip() trong Python

Top