Why do This
列舉,作為管理常量的有效手段之一,在各大主流語言中都有對應的語言級別的語法。但是在 Python 中沒有這個語法,一般來說,大家比較傾向使用『module級別的常量』來處理,這種做法,自然既簡單又粗暴又有效。但是如果需要管理大量常量,在常見的業務系統中,這個特別常見,僅僅使用上面的『三板斧』是有點不夠的。
來看看 Python 3 標準庫的 enum。嘴上說不要,心裡確實很誠實,Python總算是將其加入到語言中了,雖然是以一種庫的形式。首先我們來看看其使用方式:
import enum
class DirectionEnum(enum.IntEnum):
"""
直接繼承IntEnum,這是很常見的需求
"""
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
class DirectionEnum2(enum.Enum):
"""
繼承更加底層的Enum類
讓自己的實現更加靈活
"""
UP = `UP`
DOWN = `DOWN`
LEFT = `LEFT`
RIGHT = `RIGHT`
if __name__ == `__main__`:
somevalue = 1
# 1. 第一種使用方式,OK,很符合我們的使用習慣,並且是work的
if somevalue == DirectionEnum.UP:
# 會執行, 猜測是, IntEnum內部做了一定的拆包動作
print(`yes`)
somevalue2 = `UP`
# 2. 第一種使用方式,OK,很符合我們的使用習慣,然而得到的,卻不是正確結果
if somevalue2 == DirectionEnum2.UP:
# 不會執行, 沒有使用 .value
print(`yes2`)
# 3. 第三種使用方式,OK,並不是非常符合我們的習慣,但是,得到的預期結果是正確的
if somevalue2 == DirectionEnum2.UP.value:
# 執行, 使用了 .value
print(`yes2`)
複製程式碼
從上面我們可以看到,如果僅僅是一些 int 型別的列舉集合,那麼使用 IntEnum 是 OK 的。如果列舉集合的成分比較複雜,那麼就要使用 Enum,雖然可以得到預期的正確結果,但是卻要改改自己的使用習慣,強迫自己打出DirectionEnum2.UP.value
這樣的組合。
然而,作為腦瓜子不好使的人來說,DirectionEnum2.UP.value
這種我很難記住,而且也不符合我的直覺啊,另外,我的大部分場景,只是需要『安安靜靜地使用一個列舉』而已,看 enum 庫的內部實現,又是元類,又是委託啥的,各種高科技都用上了。所以,我們就有強大的動力,去適應自己,而不是適應別人。
How to do
考慮到我們的使用場景非常單一 & 簡單,所以就想到了如下實現
import functools
class BaseEnum:
@classmethod
@functools.lru_cache(maxsize=1024)
def values(cls):
d = cls.__dict__
return [v
for k, v in d.items()
if (not k.startswith(`_`)) and k.isupper()]
複製程式碼
然後使用之,這裡我們通過寫UT的形式去學習使用介面:
from unittest import TestCase
import py3utils
class DirectionEnum(py3utils.BaseEnum):
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
class TestBaseEnum(TestCase):
def test_values_is_list(self):
self.assertTrue(isinstance(DirectionEnum.values(), list))
def test_values(self):
e = [1, 2, 3, 4]
actual = DirectionEnum.values()
self.assertEqual(e, actual)
def test_enum(self):
actual = DirectionEnum.UP
expected = 1
self.assertEqual(actual, expected)
複製程式碼
Finally
一些平時使用得比較趁手得程式碼 snippet,最好積累起來,放入自己特定得程式碼倉庫中,以後想起來了,直接複用之即可。比如我,
就將上面得snippets 加入到自己的倉庫中 https://github.com/hezhiming/py3utils
,並佐以比較完善的的UT,以備不時之需。:)