原因与初衷
首先便是业务代码中会出现巨量的 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(并没有什么意义)。