Câu 1Lợi thế của việc thay thế so sánh string literal "paid" bằng Enum là gì?
enum và dataclasses — Hằng số có tên và Data Class
Bài viết này là một phần của Khóa học Python, giúp bạn thành thạo từ đầu những kỹ năng cần thiết để sử dụng Python, từ cơ bản đến nâng cao.
Học Enum thay thế chuỗi hằng, IntEnum + auto() cho đánh số tự động và sắp xếp, @dataclass tự sinh boilerplate, và field(default_factory=list) qua ví dụ thật.
Hai module để làm cho ý nghĩa của giá trị trở nên rõ ràng. enum cho bạn thay thế các string literal rải rác trong code bằng hằng số có tên, nên lỗi gõ sai sẽ lộ ra lúc runtime thay vì lặng lẽ trôi qua. dataclasses cho cách định nghĩa "class chỉ chứa dữ liệu" trong một dòng, loại bỏ boilerplate __init__ / __eq__ / __repr__.
Enum — thay thế string literal bằng hằng số có tên
Khi các string literal như "paid" / "shipped" rải rác trong code, lỗi gõ sai kiểu "shippped" lọt qua đến runtime, autocomplete không giúp được, và refactor dễ vỡ. Enum giải quyết điều này bằng định nghĩa tập hằng số làm class — các tên bạn định nghĩa autocomplete được trong editor, lỗi gõ sai raise AttributeError ngay lập tức, lặp qua mọi member hoạt động sẵn, và bạn được giá trị an toàn để dùng làm khóa dict hay nhãn switch.
# Các chuỗi trạng thái rải rác trong code
if status == "paid":
ship_order(order_id)
elif status == "shippped": # Lỗi gõ — đáng lẽ là "shipped", nhưng bạn không thấy đến runtime
notify_shipped(order_id)
elif status == "cancelled":
refund(order_id)
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
CANCELLED = "cancelled"
# Truy cập member: mỗi member mang một name và một value
print(OrderStatus.PAID) # OrderStatus.PAID
print(OrderStatus.PAID.name) # 'PAID'
print(OrderStatus.PAID.value) # 'paid'
# So sánh với ==
status = OrderStatus.PAID
if status == OrderStatus.PAID:
print("Đã nhận thanh toán")
IntEnum và auto — đánh số tự động và so sánh số
IntEnum là Enum kế thừa từ int, nên các member so sánh và làm phép số học như integer. Đây là lựa chọn đúng cho hằng số mà thứ tự số quan trọng — độ ưu tiên, level, rank. auto() là hàm gán giá trị tự động thay vì viết bằng tay: xếp hàng các lệnh gọi auto() và chúng thành 1, 2, 3, 4... theo thứ tự định nghĩa.
Khi con số cụ thể không quan trọng (chỉ thứ tự quan trọng), auto() an toàn hơn vì thêm hoặc bớt member không cần viết lại số bằng tay.
1, 2, 3, .... IntEnum so sánh như int, nên Priority.LOW < Priority.HIGH hoạt động — thứ tự số mã hóa độ ưu tiên.@dataclass — định nghĩa class chứa dữ liệu trong một dòng
Viết bằng tay một "class thuần chỉ có field" nghĩa là định nghĩa __init__ để gán thuộc tính, __eq__ (dunder method được gọi cho so sánh ==) cho bằng nhau theo nội dung, __repr__ (dunder method được gọi bởi print và REPL) để hiển thị dễ đọc, v.v. — cùng pattern boilerplate xếp hàng mỗi lần. Một decorator @dataclass duy nhất sinh tất cả thứ đó tự động từ các khai báo field có type hint.
Nói cách khác, chỉ cần xếp hàng các thuộc tính có type hint là xây xong cả class — bạn được cùng tính năng với code ít hơn nhiều so với class viết tay.
Dùng default_factory cho mặc định mutable
Cố mặc định một list với @dataclass bằng cách viết items: list = [] raise SyntaxError (hoặc cảnh báo deprecation). Đó là một biện pháp phòng vệ cho bug kinh điển "mọi instance cùng chia sẻ một list". Hãy dùng field(default_factory=list) thay vào đó, tạo list rỗng mới cho mỗi instance, an toàn. Cùng ý cho dict / set: field(default_factory=dict).
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Lợi thế của auto() trong IntEnum là gì?
Câu 3Cách đúng để mặc định một field thành list trong @dataclass là gì?
Câu 4@dataclass(frozen=True) làm gì?