使用 enum 列舉
April 29, 2022如果想要列舉值,雖然可以透過 dict
或者是類別來定義,不過並不方便。
沒有 enum 之前
例如使用 dict
的情況:
>>> Action = {
... 'stop' : 1,
... 'right': 2,
... 'left' : 3,
... 'up' : 4,
... 'down' : 5
... }
>>> Action['stop']
1
>>> Action['down']
5
>>>
或者是使用類別定義的方式:
>>> class Action:
... stop = 1
... right = 2
... left = 3
... up = 4
... down = 5
...
>>> Action.right
2
>>> Action.left
3
>>>
基本上這兩種方式是可以解決問題,不過問題在於,無法檢查列舉值是否重複,可以透過 Action['up'] = 5
或者是 Action.up = 5
這樣的方式來修改列舉值,如果透過類別方式來定義,Action
類別本身還能夠實例化,這些都是使用上的一些困擾。
使用 enum 模組
從 Python 3.4 開始新增了 enum
模組,其中提供了 Enum
、IntEnum
等類別,可以用來繼承以便定義列舉。繼承 Enum
的話,列舉值可以是各種型態,不過建議使用狀態不可變的值(例如字串),繼承 IntEnum
的話,列舉值就只能是整數。例如:
>>> from enum import IntEnum
>>> class Action(IntEnum):
... stop = 1
... right = 2
... left = 3
... up = 4
... down = 5
...
>>> Action.left
<Action.left: 3>
>>> Action()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __call__() missing 1 required positional argument: 'value'
>>> Action.left = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Winware\Python39\lib\enum.py", line 389, in __setattr__
raise AttributeError('Cannot reassign members.')
AttributeError: Cannot reassign members.
>>>
可以看到,你無法使用 Action()
來建立一個物件,也無法重新指定列舉值,實際上,Action()
是用來指定列舉值,然後傳回列舉物件,列舉物件上具有 name
與 value
,可用來取得列舉名稱與列舉值,也可以使用 []
指定列舉名稱來取得列舉物件。例如:
>>> Action(3)
<Action.left: 3>
>>> enum_member = Action(3)
>>> enum_member.name
'left'
>>> enum_member.value
3
>>> Action['left']
<Action.left: 3>
>>>
繼承了 Enum
或 IntEnum
而定義的類別,可以使用 for in
來迭代:
>>> for member in Action:
... print(member.name, '\t: ', member.value)
...
stop : 1
right : 2
left : 3
up : 4
down : 5
>>>
繼承 Enum
或 IntEnum
類別定義列舉時,列舉名稱不得重複,然而,列舉值可以重複。例如:
>>> class Action(IntEnum):
... stop = 1
... stop = 2
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in Action
File "C:\Winware\Python39\lib\enum.py", line 99, in __setitem__
raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'stop'
>>> class Action(IntEnum):
... stop = 1
... left = 1
...
>>> Action(1)
<Action.stop: 1>
>>> Action['left']
<Action.stop: 1>
>>>
如果列舉名稱不同然而值相同,那麼後者會是前者的別名,因此就上例來說,無論使用 Action(1)
或 Action['left']
,一律傳回 <Action.stop: 1>
。
如果想在列舉時值不得重複,可以在類別上加註 enum
模組的 @unique
,這麼一來若列舉時有重複的值,就會引發 ValueError
。例如:
>>> from enum import IntEnum, unique
>>> @unique
... class Action(IntEnum):
... stop = 1
... right = 2
... left = 3
... up = 4
... down = 4
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "C:\Winware\Python39\lib\enum.py", line 884, in unique
raise ValueError('duplicate values found in %r: %s' %
ValueError: duplicate values found in <enum 'Action'>: down -> up
>>>
如果不在乎值,只是單純想列舉名稱,也可以採用呼叫的方式來建立列舉,例如:
>>> Action = IntEnum('Action', ('stop', 'right', 'left', 'up', 'down'))
>>> Action
<enum 'Action'>
>>> Action.stop
<Action.stop: 1>
>>> Action.right
<Action.right: 2>
>>> list(Action)
[<Action.stop: 1>, <Action.right: 2>, <Action.left: 3>, <Action.up: 4>, <Action.down: 5>]
enum
模組的官方說明文件,還有一些關於列舉的相關說明,有興趣的話可以進一步參考。