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ếtreturn 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
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]:0 vì điều tương tự
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
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]: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
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
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]:2
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
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ảnBạ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
- So sánh giá trị với các enum khác dẫn đến kết quả không mong muốn
- Thật khó để lặp lại các giá trị
- Các biểu diễn chuỗi chỉ là các giá trị khó đọc
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]:3
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
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ọnDjango 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 >> 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]:4 và
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
from enum import Enumclass HttpStatus[Enum]:5
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
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
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]:Bây giờ… tôi nên lấy cái nào đây?
self.color = color
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 🤗