Python,寫一個簡單的屬於自己的『BaseEnum』類

浮生若夢的程式設計發表於2019-03-04

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,以備不時之需。:)

相關文章