Tại sao nên sử dụng enum thay vì String Python?

Một Enum là một tập hợp các giá trị tĩnh, thường là các chuỗi hoặc số nguyên. Chúng được sử dụng khá thường xuyên - hoặc ít nhất chúng nên được. Tại Cashlink, chúng tôi thường sử dụng chúng để theo dõi các bước cho các quy trình dài hơn

Enums là một cách để nhóm các hằng số. Các hằng số rất hữu ích để ngăn lỗi chính tả vì trình soạn thảo của bạn có thể cảnh báo bạn khi một giá trị không phải là một phần của enum, nhưng nó không thể cảnh báo bạn rằng một chuỗi hoặc một số nguyên không hợp lệ. Nhóm các hằng số trong Enums mang lại cho bạn một số lợi thế

Trước khi chúng ta đi sâu vào nó, hãy xem xét một số ứng dụng cho enums

Ví dụ về Enum
  • trạng thái. Bạn đang viết một chương trình phụ trợ và bạn muốn trả lại mã trạng thái, đây phải là một enum. Thay vì viết return 404 bạn viết return status.HTTP_404_NOT_FOUND. Nói chung, các biến chứa một tập hợp hữu hạn các trạng thái có thể có giá trị là Enum. Tìm "trạng thái" hoặc "trạng thái"
  • Lựa chọn. Đó có thể là lời chào mà người dùng có thể chọn hoặc vai trò của người dùng. Quốc gia xuất xứ, múi giờ,…. Tìm kiếm “lựa chọn” hoặc đôi khi chỉ là danh sách trong mã của bạn
  • các loại. Bạn có thể có các loại người dùng, đăng ký, sản phẩm, …
  • bước. Nếu bạn theo dõi kênh người dùng, bạn có thể có hằng số cho mỗi bước. Tùy thuộc vào đó bạn có thể có hành vi khác nhau. Tập hợp tất cả các bước có thể là một Enum
  • cờ. Hệ thống quyền Unix đại diện cho Đọc/Ghi/Thực thi dưới dạng cờ nhị phân trong chuỗi 3 bit là một ví dụ khác về những gì bạn muốn thực hiện với Enum
Bạn muốn làm gì với enums

Nhận hỗ trợ biên tập viên

Bạn luôn có thể chỉ cần sử dụng một hằng số. Nhưng khi bạn sử dụng trình chỉnh sửa, bạn thường muốn trình chỉnh sửa của mình tự động hoàn thành

Trình bày đẹp khi in

Nên có một cách tốt để chuyển đổi nó thành một chuỗi. Ví dụ: khi bạn in mã trạng thái HTTP, tôi nghĩ NOT_FOUND tốt hơn nhiều so với 404

Lặp lại tất cả các giá trị

Một enum là một tập hợp các giá trị tĩnh. Trong một số trường hợp, bạn muốn lặp lại tất cả chúng

Kiểm tra toàn diện

Trong rất nhiều cơ sở mã, bạn có thể thấy mẫu này

if value == 'a':
...
elif value == 'b':
...
elif value == 'c':
...
else:
# Safeguard if a case was forgotten
raise NotImplemented(f"The value {value} should not exist")

Tôi cũng đã xem

from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR
0 vì điều tương tự

Tùy thuộc vào nguồn gốc của những giá trị đó, bạn có thể so sánh

from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR
1 với một enum. Sau đó, mypy có thể thực hiện kiểm tra toàn diện, nghĩa là nó sẽ hiểu rằng có khả năng gặp phải ngoại lệ. Do đó, mypy có thể cảnh báo bạn rằng bạn thực sự đã quên một trường hợp

So sánh có ý nghĩa ngữ nghĩa

So sánh giá trị thì dễ, nhưng so sánh ngữ nghĩa thì khó. Chúng tôi muốn so sánh những thứ cùng loại, không phải giá trị. Ví dụ: chúng tôi muốn so sánh xem khoản thanh toán chúng tôi nhận được có bằng số tiền chúng tôi mong đợi hay không. Nó phải ở trong cùng một đơn vị. Sẽ tạo ra sự khác biệt lớn nếu chúng ta mong đợi 1000 Euro hoặc 1000 Rupiah Indonesia. Nó cũng tạo ra sự khác biệt lớn đối với một số người nếu bạn nói về giới tính sinh học hoặc bản sắc tình dục. Điều này có nghĩa là hai biến có thể chứa chính xác cùng một giá trị, nhưng không thể so sánh được. Ít nhất chúng tôi mong đợi một sự so sánh để đánh giá

from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR
2

Tuần tự hóa và giải tuần tự hóa

Chúng ta cần chuyển đổi dữ liệu thường xuyên sang JSON và ngược lại từ JSON. Pydantic khá tuyệt vời và luôn làm rất tốt việc ép buộc các giá trị phù hợp, nhưng hãy kiểm tra các tùy chọn đã cho xem chúng hoạt động tốt như thế nào

số 1. Mô-đun Enum tích hợp

Mô-đun enum đã được giới thiệu với Python 3. 4 qua PEP 435. Đó là điều đầu tiên xuất hiện trong đầu bạn khi bạn muốn tạo một Enum.

from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR

Cờ số nguyên có thể được thực hiện với

Nếu bạn muốn kiểm tra tính toàn diện, bạn có thể lấy cái này

from typing import NoReturndef assert_never(value: NoReturn) -> NoReturn:
assert False, f'Unhandled value: {value} ({type(value).__name__})'
def print_status(status: HttpStatusBuiltin) -> None:
if status is HttpStatusBuiltin.SUCCESS:
print('SUCCESS')
elif status is HttpStatusBuiltin.NOT_FOUND:
print('NOT_FOUND')
else:
assert_never(status)

Cung cấp cho bạn thông báo lỗi này

$ mypy main.py
main.py:35: error: Argument 1 to "assert_never" has incompatible type "Literal[HttpStatus.INTERNAL_SERVER_ERROR]"; expected "NoReturn"

Bạn có thể làm cho Enums có thể tuần tự hóa JSON, nhưng bạn phải suy nghĩ một chút

Chuyển đổi Python Enum thành JSON

Một cách đơn giản để tạo Python Enum dựa trên mã thông báo chuỗi JSON-serializable là kế thừa cả str và Enum…

py. đồng hồ

số 2. Thuộc tính lớp đơn giản

Bạn có thể chỉ cần tạo một lớp và sử dụng các thuộc tính của nó

class HttpStatus:
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
200
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
True

Mặc dù những thứ như thế này vẫn còn phổ biến trong nhiều cơ sở mã, nhưng nhược điểm là rõ ràng

  1. So sánh giá trị với các enum khác dẫn đến kết quả không mong muốn
  2. Thật khó để lặp lại các giá trị
  3. Các biểu diễn chuỗi chỉ là các giá trị khó đọc
số 3. Các hằng số trong một mô-đun

Bạn có thể đặt các giá trị trực tiếp trong một mô-đun, e. g

from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR
3

NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500

Đây có lẽ là tùy chọn nhẹ nhất. Bạn vẫn có tất cả các nhược điểm

Một ví dụ thực tế nơi điều này được thực hiện là Django Rest Framework với mã trạng thái HTTP ( nguồn )

số 4. mô hình Django. Lựa chọn

Django cũng có một số cách để tạo enums, e. g.

from django.db import modelsclass HttpStatus(models.TextChoices):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
404
200
500
# Comparisons
>>> HttpStatus.NOT_FOUND.value == '404'
True
>>> HttpStatus.NOT_FOUND == '404'
True
>>> HttpStatus.NOT_FOUND.value <= '404'
True
# Custom functions
>>> HttpStatus.choices
[('404', 'Not Found'), ('200', 'Success'), ('500', 'Internal Server Error')]
>>> HttpStatus.names
['NOT_FOUND', 'SUCCESS', 'INTERNAL_SERVER_ERROR']
>>> HttpStatus.labels
['Not Found', 'Success', 'Internal Server Error']
>>> HttpStatus.values
['404', '200', '500']

Ngoài ra còn có

from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR
4 và
from enum import Enumclass HttpStatus(Enum):
NOT_FOUND = 404
SUCCESS = 200
INTERNAL_SERVER_ERROR = 500
# Printing
>>> print(HttpStatus.SUCCESS)
"HttpStatus.SUCCESS"
# Comparisons
>>> HttpStatus.NOT_FOUND == 404
False
>>> HttpStatus.NOT_FOUND.value == 404
True
# Iteration
>>> for value in HttpStatus:
.. print(value)
..
HttpStatus.NOT_FOUND
HttpStatus.SUCCESS
HttpStatus.INTERNAL_SERVER_ERROR
5

Django TextChoices Enums thêm một số chức năng có thể hữu ích. Tôi khuyên bạn nên sử dụng chúng nếu bạn sử dụng Django. Chúng có thể được kết nối với Django ORM, điều này khá hay

Các lựa chọn Django cũng có thể tuần tự hóa JSON trực tiếp

số 5. đánh máy. nghĩa đen

The được giới thiệu trong PEP 586 như một cách để chỉ ra rằng một biến phải có một tập hợp các giá trị nhất định. Ví dụ, thay vì chỉ nói rằng một hàm sẽ trả về một số nguyên làm tham số, bạn có thể chú thích điều này

from typing import LiteralStatusCode = Literal[404, 200, 500]def set_color(self, http_status_code: StatusCode):
self.color = color
Bây giờ… tôi nên lấy cái nào đây?

Trước tiên, hãy đảm bảo loại Enum đáp ứng nhu cầu của bạn

Hình ảnh của Martin Thoma

Ngoài ra còn có một số lựa chọn khác

  • Bạn có cần hỗ trợ Python 3 không. 3 hay sớm hơn?
  • Bạn chỉ muốn cung cấp cho mypy khả năng kiểm tra sự cố tốt hơn?
  • Bạn có sử dụng Django không?

Tôi thích viết về phát triển phần mềm và công nghệ 🤩 Đừng bỏ lỡ các bản cập nhật. Nhận bản tin email miễn phí của tôi 📧 hoặc đăng ký Phương tiện ✍️ nếu bạn chưa làm điều đó - cả hai đều khuyến khích tôi viết nhiều hơn 🤗

Tại sao sử dụng enums thay vì chuỗi?

Vì enum được lưu trữ trong cơ sở dữ liệu dưới dạng số nguyên thay vì chuỗi, nên các nhà phát triển cũng chỉ ra rằng chúng chiếm ít dung lượng hơn .

Lợi ích của việc sử dụng enum trong python là gì?

Viết tắt của kiểu liệt kê, tính năng ngôn ngữ này xác định một tập hợp tên được liên kết với các giá trị không đổi như số, chuỗi, v.v. Các enum Python rất hữu ích để biểu thị dữ liệu đại diện cho một tập hợp trạng thái hữu hạn chẳng hạn như ngày trong tuần, tháng trong năm, v.v. Chúng đã được thêm vào Python 3. 4 qua PEP 435.

Tại sao sử dụng enum thay vì từ điển python?

Enum được sử dụng để tạo một tập hợp các hằng số liên quan . Từ điển được sử dụng để lưu trữ các giá trị dữ liệu trong khóa. cặp giá trị. Enum tương tự như một mảng. Từ điển không cho phép trùng lặp.

Là enums nhanh hơn chuỗi?

Enums được sử dụng để giữ trạng thái và thường được so sánh, và so sánh enum cực kỳ nhanh. Nhanh hơn nhiều so với việc so sánh các chuỗi .