在 Python 中使用 enum

懒人在思考 2017-12-03

原因与初衷

首先便是业务代码中会出现巨量的 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)

总结

其实更加希望在语言级别提供常量管理的常用手段,而不是为了 simple 而 simple(并没有什么意义)。

相关推荐