# enum 【列舉 Class】

## 簡介

***

### What is Enum?

Python 中的 `enum` module 是 standard library 的一部分，並提供對建立 enumerations 的支援，枚舉是綁定到唯一常數值的一組符號名稱（成員）。

Enum 是 Enumeration 的縮寫，是 Python 中的一個 class，用於建立 enumerations；是一組綁定到唯一值的符號名稱。每個 enumeration member 都有一個 name 和一個 value。 `enum` module 定義了 5 個 enumeration classes，可用來建立唯一的 names 和 values sets：`Enum`、`IntEnum`、`Flag` 和 `IntFlag`。

Enumeration 的 members 可以透過這些符號名稱進行比較，使程式碼更具可讀性和可維護性。

### How to Use Enum?

Enumerations 是 immutable (不可變的)，即它們不能更改。 一旦它們被建立，它們就保持不變。 Enumeration 是一組值的符號名稱。 Enumerations 是 iterable，可以使用迴圈來遍歷它們。

要定義 enumeration,可以透過 2 種方式：

1. 繼承 `Enum` Class。
2. 將名稱 sequence 傳遞給 `Enum()` 函數。(函數式 API，語法類似 [collections.namedtuple](https://docs.python.org/3.9/library/collections.html#collections.namedtuple))

{% code title="PYTHON" %}

```python
from enum import Enum

# Method 1
class Color(Enum):
    RED = 1    # Enum member: RED
    GREEN = 2  # Enum member: GREEN
    BLUE = 3   # Enum member: BLUE

print(Color.RED)        # Output: Color.RED
print(Color.RED.name)   # Output: RED
print(Color.RED.value)  # Output: 1

# Method 2
Animal = Enum('Animal', 'CAT DOG BIRD')

print(Animal.CAT)        # Output: Animal.CAT
print(Animal.CAT.name)   # Output: CAT
print(Animal.CAT.value)  # Output: 1

for i in Animal:
    print(i)
'''
Output: 
Animal.CAT
Animal.DOG
Animal.BIRD
'''
```

{% endcode %}

您可能使用 `1`、`2` 和 `3`， 來表示任務的狀態："正在進行"、"已完成"和"未開始"。

現在，您可以在程式碼中，使用 `TaskStatus.IN_PROGRESS`、`TaskStatus.COMPLETED` 和`TaskStatus.NOT_STARTED`，它們比原始數字更容易理解。

{% code title="" %}

```python
from enum import Enum

class TaskStatus(Enum):
    IN_PROGRESS = 1
    COMPLETED = 2
    NOT_STARTED = 3
```

{% endcode %}

{% hint style="info" %}
**Enumerations 是 Class，但它們通常用於建立唯一的常數集合 (建立實例)，並且通常沒有方法，儘管包含方法是完全有效的。**
{% endhint %}

{% hint style="warning" %}
**對於大多數新程式碼，強烈建議使用 `Enum` 和 `Flag`。**

因為 `IntEnum` 和 `IntFlag` 破壞了 enumeration 的一些語義承諾 (透過與整數進行比較，與其他不相關 enumerations 的傳遞性)。 `IntEnum` 和 `IntFlag` 應當僅在 `Enum` 和 `Flag` 不起作用的情況下使用。 例如：當整數常數被替換為 enumerations 時，或者為了與其他系統進行 interoperability 操作時。
{% endhint %}

### Why Use Enum?

Enums 用於理解的術語 (terms) 表達的特定概念，並且還提供一系列功能，使它們比簡單的常數和 String 更強大且更易於維護。

如果使用 String 容易出現錯別字，如果不查看文檔或實現，就無法立即清楚有效的 Strings 是什麼，並且 IDE 中沒有自動完成支持。

透過定義 enum Class，您可以清楚地了解可以應用了哪些類型，並且更難犯錯誤。 您可以獲得 IDE 中的自動完成等好處，您不會拼錯 string，並且將來可以輕鬆新增或更改 enum Class。

使用 enumerations 的主要好處包括：

1. **提高代碼可讀性和可維護性**：Enums 允許開發人員使用命名值，而不是容易混淆或不清楚的數字或 String，從而提高了可讀性。
2. **安全或防止錯誤**：使用 enums，可以減少因為傳遞無效參數而導致的錯誤。如果你定義了一個期望使用枚舉的函數或方法，那麼它只能接收在枚舉中定義的值。
3. **Enumeration Iteration**：Enum members 可以使用循環進行 iterated。 這允許更動態地處理 enum 值並且可以簡化代碼。
4. **豐富的 Comparisons**：Enum members 可以透過這些符號運算子進行比較，還可以進行 sorted 和 hashed。

{% hint style="success" %}
**在較小、較簡單的程序中這些好處可能不會立即顯現出來，但在較大、較複雜的應用程序中它們變得非常重要，並且通常被認為是一種良好的做法。**
{% endhint %}

{% hint style="info" %}
**Python vs. C/C++ enumerations。**&#x20;

在 C/C++ 中，enumerations 是一種創建新類型的方法，它由一組命名的整數組成，enumeration 本身是整數類型。 在 Python 中，enumeration 的每個成員本身就是一個不同的值，而不僅僅是整數的別名。
{% endhint %}

### When to Use Enum?

當您想要將一組相關的值組合在一起，並以一致的方式引用時，Enums 特別有用。例如：

* State machine 中的狀態。
* GUI 或 Web 應用程式的下拉式選單中的選項。
* Categories, types or labels。 (例如: 產品類別)
* Status codes or error codes。(例如: 錯誤和異常處理)
* Handling Configuration Settings。

### Conclusion

Python 中的 `enum` module 是創建 enumerable 常數的強大工具。透過使用 enums，開發人員可以編寫更易於閱讀、維護且不易出錯的程式碼。當處理一組代表某種狀態、類型或類別的預定義值時，它特別有用。

## Enumeration data

***

**Enumeration data**: Enumeration 資料、enumerated 類型或 enum 是一種資料類型。 這是一個簡單的enumeration 範例：

{% code title="PYTHON" %}

```python
from enum import Enum

class Color(Enum):
    RED = 1
    BLUE = 2
    GREEN = 3

print(Color.RED.name)   # Output: "RED"
print(Color.RED.value)  # Output: "1"
```

{% endcode %}

**enumeration:** `Color` **enumeration members:** `RED`, `BLUE`, `GREEN` 成員的名稱按照慣例為大寫，因為它們被視為常數。每個 enumeration member 都有兩個重要的屬性：`name` 和 `value`。 `name` 屬性給出 member name (string)，value 屬性給出成員的值。

### &#x20;Enum members, values

***

有兩個同名的 enum members 是無效的。但是，兩個 enum members 允許具有相同的值。 給定兩個具有相同值的 members `A` 和 `B`(並且 `A` 首先定義)，member `B` 是 `A` 的別名。

{% code title="PYTHON" %}

```python
import enum

class Shape(enum.Enum):
    DIAMOND = 1
    SQUARE = 2
    CIRCLE = 3
    ALIAS_FOR_SQUARE = 2

print(Shape.SQUARE)            # Output: Shape.SQUARE
print(Shape.ALIAS_FOR_SQUARE)  # Output: Shape.SQUARE
print(Shape(2))                # Output: Shape.SQUARE

class Shape(enum.Enum):
    SQUARE = 2
    SQUARE = 3

# Output: 
# TypeError: 'SQUARE' already defined as 2
```

{% endcode %}

### 範例 – 用法

***

1. 如果需要根據使用者的輸入訪問 Enum member 或其值，可以使用 `Enum.member` 來獲取該 member，或使用 `Enum(value)` 透過其值來獲取 Enum member。
2. Enumeration 值被 `repr()` 解析為 `<enum 'EnumName'>.<member_name>`，而不是它們的值。
3. Enum members 是 hashable，這意味著它們可以在需要 hashable 物件的任何 context 中使用。 這包括作為 dictionaries 中的 keys 和 Sets 中的元素，它們也是 immutable。

{% code title="PYTHON" %}

```python
from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
    YELLOW = 4

# Accessing value of Enum member
print(Color.RED.value)  # Output: 1
print(Color.RED.name)   # Output: RED

# Accessing Enum member based on its value
print(Color(1))         # Output: Color.RED

# Accessing Enum member
print(Color.RED)        # Output: Color.RED
print(Color['RED'])     # Output: Color.RED

# =========================================================
print(repr(Color.RED))       # Output: <Color.RED: 1>
print(list(Color))           # Output: [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
print(list(reversed(Color))) # Output: [<Color.BLUE: 3>, <Color.GREEN: 2>, <Color.RED: 1>]
print(dir(Color))            # Output: ['BLUE', 'GREEN', 'RED', '__class__', '__doc__', '__members__', '__module__']
print(len(Color))            # Output: 3

print(type(Color))           # <class 'enum.EnumType'>
print(type(Color.RED))       # <enum 'Color'>
print(Color.RED in Color)    # Output: True
print(isinstance(Color.GREEN, Color)) # Output: True

# =========================================================
print(Color.RED is Color.BLUE)        # Output: False
print(Color.RED is not Color.BLUE)    # Output: True

print(Color.RED == Color.BLUE)        # Output: False
print(Color.RED != Color.BLUE)        # Output: True

# Enum members are not integers 
print(Color.RED == 1)                 # Output: False
#print(Color.RED < Color.BLUE)    
#print(Color.RED <= Color.BLUE) 

# =========================================================
# Iterating Enum
for i in Color:
    print(i)
'''
Color.RED
Color.GREEN
Color.BLUE
Color.YELLOW
'''

for name, member in Color.__members__.items():
    print(name, member)
'''
RED Color.RED
GREEN Color.GREEN
BLUE Color.BLUE
YELLOW Color.YELLOW
'''

# Used for detailed programmatic access
print([name for name, member in Color.__members__.items() if member.name != None])
# Output: ['RED', 'GREEN', 'BLUE', 'YELLOW']
'''
names = []
for name, member in Color.__members__.items():
    if member.name is not None:
        names.append(name)
'''

# =========================================================
# Enumeration members are hashable
# Use Enum members as dictionary keys
color_dict = {
    Color.RED: 'It symbolizes love and anger.',
    Color.GREEN: 'It symbolizes nature and tranquility.',
    Color.BLUE: 'It symbolizes sea and sky.',}
print(color_dict[Color.RED])     # Output: 'It symbolizes love and anger.'

# Use Enum members in sets
color_set = {Color.RED, Color.BLUE}
print(Color.GREEN in color_set)  # Output: False
print(Color.RED in color_set)    # Output: True
```

{% endcode %}

## Enum

***

`Enum`  是 `enum` enumerations 的 Base Class。

`EnumType` 負責在其他 `enum` Class 上設置正確的方法，包含: `name`, `value`, `_generate_next_value_`, `__str__` 方法...。

### IntEnum

***

`IntEnum` 與 `Enum` 的用法大致相同，但它是 `Enum` 的 Subclass，且 enumeration 值是 `int` 的實例。

本質上，`IntEnum` members 可以參與所有 `int` 的 operations。如果對 `IntEnum` members 執行任何整數運算，則結果值將失去 enumeration 狀態。

{% code title="PYTHON" %}

```python
from enum import IntEnum

class Number(IntEnum):
    ONE = 1
    TWO = 2
    THREE = 3

print(Number.THREE.value)       # Output: 1
print(Number.ONE + Number.TWO)  # Output: 3
print(Number.THREE + 5)         # Output: 8

# Loses its enumeration status
print(type(Number.THREE))     # Output: <enum 'Number'>
print(type(Number.THREE + 5)) # Output: <class 'int'>
```

{% endcode %}

### StrEnum

***

`StrEnum` 與 `Enum` 的用法大致相同，但它是 `Enum` 的 Subclass，且 enumeration 值是 `str` 的實例。

本質上，`IntEnum` members 可以參與所有 `str` 的 operations。如果對 `StrEnum` members 執行任何字串運算，則結果值將失去 enumeration 狀態。

{% code title="PYTHON" %}

```python
from enum import StrEnum

class Number(StrEnum):
    ONE = "ONE"
    TWO = "TWO"
    THREE = "THREE"

print(Number.ONE.value) # Output: "ONE"

print(type(Number.TWO)) # Output: <enum 'Number'>
print(type(Number.TWO + "TWO"))        # Output: <class 'str'>
print(type(Number.TWO + Number.THREE)) # Output: <class 'str'>
```

{% endcode %}

### EnumType

***

`EnumType` 是 `enum` enumerations 的 Metaclass。 可以對 `EnumType` 進行Subclassing。

`EnumType` 負責在 `enum` 上設置正確的方法，包含: `__call__`, `__repr__`、`__str__`、`__format__`, `__reduce__`, `__len__`, `__iter__` 方法...。

### 範例 – 衍生 Enumerations

***

透過擴展，不同類型的 `IntEnum` 也可以相互比較。但是，`IntEnum` 仍然無法與標準 `Enum` Enumerations 進行比較。

`IntEnum` 值的行為，也可以類似於整數。

{% code title="PYTHON" %}

```python
from enum import IntEnum, Enum

class Shape(IntEnum):
    CIRCLE = 1
    SQUARE = 2

class Request(IntEnum):
    POST = 1
    GET = 2

print(Shape.CIRCLE == 1)             # Output: True
print(Shape.CIRCLE == Request.POST)  # Output: True

# =========================================================
class Color(Enum):
    RED = 1
    GREEN = 2
    
print(Shape.CIRCLE == Color.RED)     # Output: False

# =========================================================
# IntEnum To int
print(int(Shape.CIRCLE))                # Output: 1
# IntEnum To index
print(['a', 'b', 'c'][Shape.CIRCLE])    # Output: "b"
# IntEnum To len()
print([i for i in range(Shape.SQUARE)]) # Output: [0, 1]

```

{% endcode %}
