原因與初衷
首先便是業務程式碼中會出現巨量的 XXType 之類的東西,業務越複雜,這種東西就越多,它們符合 enum 的基本假定,用 enum 去管理這些常量是很自然的想法。
其次便是 Python 並沒有實現 enum 這一抽象手段(其實我極其希望加入這一特性,另一個希望加入的特性是 const )。
常用的做法
方式1:模組級別常量:
這也是標準庫和許多流行庫的做法,比如:
# direction.py
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
複製程式碼
方式2:使用 enum 庫
Python 2 和Python 3 均可使用,Python 3 更是直接變為標準庫,從側面可知道 enum 這種東西的重要性。
典型使用:
import enum
import unittest
class BaseEnum(enum.Enum):
"""一般都會再次封裝下, 後文詳述原因"""
@classmethod
def values(cls):
"""獲取右邊value的集合
因為我們經常要用到 xxvalue in xxEnum.values(), 這是我們
封裝的原因與初衷
:return:
"""
values = []
for attr in cls:
values.append(attr.value)
return values
class DirectionEnum(BaseEnum):
"""使用"""
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
######################### 單元測試 ##########################
class TestBaseEnum(unittest.TestCase):
def test_values(self):
expected = frozenset((DirectionEnum.UP.value, DirectionEnum.DOWN.value,
DirectionEnum.LEFT.value, DirectionEnum.RIGHT.value))
actual = frozenset(DirectionEnum.values())
self.assertSetEqual(expected, actual)
def test_value_in(self):
value = 1
self.assertTrue(value in DirectionEnum.values())
def test_value_equal(self):
value = 1
self.assertTrue(value == DirectionEnum.UP.value)
複製程式碼
我的做法
上面的幾種做法已經覆蓋得很全面了,我完全沒必要自己再去弄一個醜陋的輪子。
之所以不用方式1,因為 module 對我而言是一個很重要的名稱空間,直接把常量掛載在其下面,對我而言是一種浪費。
之所以不用方式2,完全是因為我有點不喜歡 xxvalue == DirectionEnum.UP.value
這種寫法(難道不是 xxvalue == DirectionEnum.UP
這種寫法更加自然嗎?)。但是總的來說,enum 庫畢竟是大神的作品,而且也實現了 enum 這一概念的的方方面面,我們自然可以放心使用,而且我也推薦使用。:)
我最終還是選擇了一種適合自己業務場景的一種使用方式,雖然土,但總還算是 work。
import unittest
import abc
class BaseEnum(abc.ABC):
@abc.abstractclassmethod
def values(cls):
cls_dict = cls.__dict__
return [cls_dict[key]
for key in cls_dict.keys()
if not key.startswith(`_`) and key.isupper()]
class DirectionEnum(BaseEnum):
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
######################## 單元測試 ################################
class TestBaseEnum(unittest.TestCase):
def test_values(self):
expected = frozenset([DirectionEnum.UP, DirectionEnum.DOWN,
DirectionEnum.LEFT, DirectionEnum.RIGHT])
actual = frozenset(DirectionEnum.values())
self.assertSetEqual(expected, actual)
def test_value_in(self):
value = 1
self.assertTrue(value in DirectionEnum.values())
def test_value_equal(self):
value = 1
self.assertTrue(value == DirectionEnum.UP)
複製程式碼
生產環境中的用法
目錄結構一般我會定義如下
app/
constants/
__init__.py 把所有子檔案中的 API 匯入到package級別, 方便客戶端呼叫
enum_constants.py 所有XXEnum都在這裡面, 檔案命名不重要, 反正我會提升到package級別
xx_contants.py
複製程式碼
使用
from constants import DirectionEnum, XXEnum, YYEnum
def func():
if somevalue == DirectionEnum.UP:
# do something
複製程式碼
總結
其實更加希望在語言級別提供常量管理的常用手段,而不是為了 simple 而 simple(並沒有什麼意義)。