【python】Enum 列舉類

hd092336發表於2024-07-16

Enum 列舉類[1]

Enum 是一組與互不相同的值分別繫結的符號名,類似於全域性變數。因為列舉通常表示常量,所以建議列舉成員命名時採用大寫

定義

  1. 類定義

    class Color(Enum):  # class syntax
        RED = 1
        GREEN = 2
        BLUE = 3
    
  2. 方法定義

    Color = Enum('Color', ['RED', 'GREEN', 'BLUE'])  # functional syntax
    
    • 列舉的成員可以是一個用空格或逗號分隔的字串(值預設auto):

      'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'

    • 或是一個名稱的迭代器物件: ['RED', 'GREEN', 'BLUE']

    • 或是一個 (名稱, 值) 對的迭代器物件: [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]

    • 或是一個對映物件: {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}

基本概念

Color = Enum("Color", ["RED", "GREEN"])

assert Color.RED.name == "RED"  # 成員的名稱
assert Color.GREEN.value == 2  # 成員的值
  • Color 是一個 列舉 (或 enum
  • 屬性 Color.REDColor.GREEN 等是列舉成員,並且在功能上是常量。
  • 列舉成員有 namesvalues 等屬性(Color.RED 的名稱是 REDColor.GREEN 的值是 2)

🗒️ 備註:有些地方會檢查是否是真正的 str 而不是 str 的子類 (例如使用 type(unknown) == str 而不是 isinstance(unknown, str)),在這些地方你將需要使用 str(StrEnum.member)

基本使用方式

Color = Enum("Color", ["RED", "GREEN"])

assert Color(1) == Color.RED  # 透過值查詢現有成員
assert Color["GREEN"] == Color.GREEN # 透過名稱查詢現有成員
assert Color.RED in Color  # __contains__
assert list(Color) == [i for i in Color]  # __iter__,[<Color.RED: 1>, <Color.GREEN: 2>]
assert len(Color) == 2  # __len__,返回 cls 中成員的數量
list(reversed(Color))  # __reversed__,按定義的逆序返回 cls 中的每個成員

特殊使用方式

auto()

enum.auto 例項被替換為列舉成員的適當值。

  • StrEnum 預設為成員名稱的小寫版本,
  • 而其他列舉預設為 1 並由此遞增。

_missing_(cls, value) 預設值處理

一個用來查詢不存在於 cls 中的值的類方法。 在預設情況下它將不做任何事,但可以被重寫以實現自定義的搜尋行為:

from enum import StrEnum, auto

class Build(StrEnum):
    DEBUG = auto()
    OPTIMIZED = auto()
    UNKNOWN = auto()

    @classmethod
    def _missing_(cls, value):
        value = value.lower()
        for member in cls:
            if member.value == value:
                return member
        return cls.UNKNOWN

print(Build.DEBUG)  # debug
print(Build('deBUG'))  # debug
print(Build('dev'))  # unknown

__init__ vs __new__

如果定義了 __new__()__init__(),則列舉成員的值將被傳給這些方法。當你想要定製 Enum 成員的實際值時必須使用 __new__()。 任何其他修改可以用 __new__() 也可以用 __init__(),應優先使用 __init__()

class Planet(Enum):
    MERCURY = (3.303e+23, 2.4397e6)
    VENUS   = (4.869e+24, 6.0518e6)
    EARTH   = (5.976e+24, 6.37814e6)
    MARS    = (6.421e+23, 3.3972e6)
    JUPITER = (1.9e+27,   7.1492e7)
    SATURN  = (5.688e+26, 6.0268e7)
    URANUS  = (8.686e+25, 2.5559e7)
    NEPTUNE = (1.024e+26, 2.4746e7)

    def __init__(self, mass, radius):
        self.mass = mass       # in kilograms
        self.radius = radius   # in meters

    @property
    def surface_gravity(self):
        G = 6.67300E-11  # universal gravitational constant  (m3 kg-1 s-2)
        return G * self.mass / (self.radius * self.radius)

Planet.EARTH.value  # (5.976e+24, 6378140.0)
Planet.EARTH.surface_gravity  # 9.802652743337129

舉例來說,如果你要向構造器傳入多個條目,但只希望將其中一個作為值:

class Coordinate(bytes, Enum):
    """
    Coordinate with binary codes that can be indexed by the int code.
    """
    def __new__(cls, value, label, unit):
        obj = bytes.__new__(cls, [value])
        obj._value_ = value
        obj.label = label
        obj.unit = unit
        return obj

    PX = (0, 'P.X', 'km')
    PY = (1, 'P.Y', 'km')
    VX = (2, 'V.X', 'km/s')
    VY = (3, 'V.Y', 'km/s')

print(Coordinate['PY'])  # Coordinate.PY
print(Coordinate(3))  # Coordinate.VY

⚠️警告:不要呼叫 super().__new__(),因為只能找到僅用於查詢的 __new__;請改為直接使用該資料型別。

普通方法和特殊方法

列舉是 Python 的類,可帶有普通方法和特殊方法。假設有如下列舉:

class Mood(Enum):
    FUNKY = 1
    HAPPY = 3

    def describe(self):
        # self is the member here
        return self.name, self.value

    def __str__(self):
        return 'my custom str! {0}'.format(self.value)

    @classmethod
    def favorite_mood(cls):
        # cls here is the enumeration
        return cls.HAPPY

旗標 Flag

為了符合規範,旗標的值必須為二的乘方,且名稱不可重複。 因此,除了別名的定義 Enum 之外,沒有值 (即 0) 或是幾個二的乘方值之和 (如 3) 的旗標也會被視為別名。

class Perm(Flag):
    R = 4
    W = 2
    X = 1
    A = R|W|X

assert (Perm.R | Perm.W).name == "R|W"  # effectively Perm(0)
assert (Perm.R & Perm.W).name is None  # effectively Perm(0)
assert bool(Perm.R & Perm(6)) is True

  1. Python 版本:3.11 ↩︎

相關文章