enum --- 枚舉類(lèi)型支持?
3.4 新版功能.
源代碼: Lib/enum.py
枚舉是一組符號(hào)名稱(chēng)(枚舉成員)的集合,枚舉成員應(yīng)該是唯一的、不可變的。在枚舉中,可以對(duì)成員進(jìn)行恒等比較,并且枚舉本身是可迭代的。
模塊內(nèi)容?
此模塊定義了四個(gè)枚舉類(lèi),它們可被用來(lái)定義名稱(chēng)和值的不重復(fù)集合: Enum, IntEnum, Flag 和 IntFlag。 此外還定義了一個(gè)裝飾器 unique() 和一個(gè)輔助類(lèi) auto。
-
class
enum.Enum? 用于創(chuàng)建枚舉型常數(shù)的基類(lèi)。 請(qǐng)參閱 Functional API 小節(jié)了解另一種替代性的構(gòu)建語(yǔ)法。
-
class
enum.IntFlag? 此基類(lèi)用于創(chuàng)建可使用按位運(yùn)算符進(jìn)行組合而不會(huì)丟失其
IntFlag成員資格的枚舉常量。IntFlag成員同樣也是int的子類(lèi)。
-
enum.unique()? 此 Enum 類(lèi)裝飾器可確保只將一個(gè)名稱(chēng)綁定到任意一個(gè)值。
-
class
enum.auto? 實(shí)例會(huì)被替換為一個(gè)可作為 Enum 成員的適當(dāng)?shù)闹怠?初始值從 1 開(kāi)始。
3.6 新版功能: Flag, IntFlag, auto
創(chuàng)建一個(gè) Enum?
枚舉是使用 class 語(yǔ)法來(lái)創(chuàng)建的,這使得它們易于讀寫(xiě)。 另一種替代創(chuàng)建方法的描述見(jiàn) Functional API。 要定義一個(gè)枚舉,可以對(duì) Enum 進(jìn)行如下的子類(lèi)化:
>>> from enum import Enum
>>> class Color(Enum):
... RED = 1
... GREEN = 2
... BLUE = 3
...
注解
Enum 的成員值
成員值可以為任意類(lèi)型: int, str 等等。 如果具體的值不重要,你可以使用 auto 實(shí)例,將為你選擇適當(dāng)?shù)闹怠?但如果你混用 auto 與其他值則需要小心謹(jǐn)慎。
注解
命名法
類(lèi)
Color是一個(gè) enumeration (或稱(chēng) enum)屬性
Color.RED,Color.GREEN等等是 枚舉成員 (或稱(chēng) enum 成員) 并且被用作常量。枚舉成員具有 名稱(chēng) 和 值 (例如
Color.RED的名稱(chēng)為RED,Color.BLUE的值為3等等)
注解
雖然我們使用 class 語(yǔ)法來(lái)創(chuàng)建 Enum,但 Enum 并不是普通的 Python 類(lèi)。 更多細(xì)節(jié)請(qǐng)參閱 How are Enums different?。
枚舉成員具有適合人類(lèi)閱讀的表示形式:
>>> print(Color.RED)
Color.RED
...而它們的 repr 包含更多信息:
>>> print(repr(Color.RED))
<Color.RED: 1>
一個(gè)枚舉成員的 type 就是它所從屬的枚舉:
>>> type(Color.RED)
<enum 'Color'>
>>> isinstance(Color.GREEN, Color)
True
>>>
Enum 的成員還有一個(gè)包含其條目名稱(chēng)的特征屬性:
>>> print(Color.RED.name)
RED
枚舉支持按照定義順序進(jìn)行迭代:
>>> class Shake(Enum):
... VANILLA = 7
... CHOCOLATE = 4
... COOKIES = 9
... MINT = 3
...
>>> for shake in Shake:
... print(shake)
...
Shake.VANILLA
Shake.CHOCOLATE
Shake.COOKIES
Shake.MINT
枚舉成員是可哈希的,因此它們可在字典和集合中可用:
>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True
對(duì)枚舉成員及其屬性的程序化訪(fǎng)問(wèn)?
有時(shí)對(duì)枚舉中的成員進(jìn)行程序化訪(fǎng)問(wèn)是很有用的(例如在某些場(chǎng)合不能使用 Color.RED 因?yàn)樵诰幊虝r(shí)并不知道要指定的確切顏色)。 Enum 允許這樣的訪(fǎng)問(wèn):
>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>
如果你希望通過(guò) name 來(lái)訪(fǎng)問(wèn)枚舉成員,可使用條目訪(fǎng)問(wèn):
>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>
如果你有一個(gè)枚舉成員并且需要它的 name 或 value:
>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1
復(fù)制枚舉成員和值?
不允許有同名的枚舉成員:
>>> class Shape(Enum):
... SQUARE = 2
... SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'SQUARE'
但是,允許兩個(gè)枚舉成員有相同的值。 假定兩個(gè)成員 A 和 B 有相同的值(且 A 先被定義),則 B 就是 A 的一個(gè)別名。 按值查找 A 和 B 的值將返回 A。 按名稱(chēng)查找 B 也將返回 A:
>>> class Shape(Enum):
... SQUARE = 2
... DIAMOND = 1
... CIRCLE = 3
... ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
<Shape.SQUARE: 2>
>>> Shape.ALIAS_FOR_SQUARE
<Shape.SQUARE: 2>
>>> Shape(2)
<Shape.SQUARE: 2>
注解
試圖創(chuàng)建具有與某個(gè)已定義的屬性(另一個(gè)成員或方法等)相同名稱(chēng)的成員或者試圖創(chuàng)建具有相同名稱(chēng)的屬性也是不允許的。
確保唯一的枚舉值?
默認(rèn)情況下,枚舉允許有多個(gè)名稱(chēng)作為某個(gè)相同值的別名。 如果不想要這樣的行為,可以使用以下裝飾器來(lái)確保每個(gè)值在枚舉中只被使用一次:
-
@enum.unique
專(zhuān)用于枚舉的 class 裝飾器。 它會(huì)搜索一個(gè)枚舉的 __members__ 并收集所找到的任何別名;只要找到任何別名就會(huì)引發(fā) ValueError 并附帶相關(guān)細(xì)節(jié)信息:
>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
... ONE = 1
... TWO = 2
... THREE = 3
... FOUR = 3
...
Traceback (most recent call last):
...
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
使用自動(dòng)設(shè)定的值?
如果確切的值不重要,你可以使用 auto:
>>> from enum import Enum, auto
>>> class Color(Enum):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
值將由 _generate_next_value_() 來(lái)選擇,該函數(shù)可以被重載:
>>> class AutoName(Enum):
... def _generate_next_value_(name, start, count, last_values):
... return name
...
>>> class Ordinal(AutoName):
... NORTH = auto()
... SOUTH = auto()
... EAST = auto()
... WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]
注解
默認(rèn) _generate_next_value_() 方法的目標(biāo)是提供所給出的最后一個(gè) int 所在序列的下一個(gè) int,但這種行為方式屬于實(shí)現(xiàn)細(xì)節(jié)并且可能發(fā)生改變。
注解
_generate_next_value_() 方法定義必須在任何其他成員之前。
迭代?
對(duì)枚舉成員的迭代不會(huì)給出別名:
>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
特殊屬性 __members__ 是一個(gè)將名稱(chēng)映射到成員的有序字典。 它包含枚舉中定義的所有名稱(chēng),包括別名:
>>> for name, member in Shape.__members__.items():
... name, member
...
('SQUARE', <Shape.SQUARE: 2>)
('DIAMOND', <Shape.DIAMOND: 1>)
('CIRCLE', <Shape.CIRCLE: 3>)
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)
__members__ 屬性可被用于對(duì)枚舉成員進(jìn)行詳細(xì)的程序化訪(fǎng)問(wèn)。 例如,找出所有別名:
>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']
比較運(yùn)算?
枚舉成員是按標(biāo)識(shí)號(hào)進(jìn)行比較的:
>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True
枚舉值之間的排序比較 不被 支持。 Enum 成員不屬于整數(shù) (另請(qǐng)參閱下文的 IntEnum):
>>> Color.RED < Color.BLUE
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'
相等比較的定義如下:
>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == Color.BLUE
True
與非枚舉值的比較將總是不相等(同樣地,IntEnum 被顯式設(shè)計(jì)成不同的行為,參見(jiàn)下文):
>>> Color.BLUE == 2
False
允許的枚舉成員和屬性?
以上示例使用整數(shù)作為枚舉值。 使用整數(shù)相當(dāng)簡(jiǎn)潔方便(并由 Functional API 默認(rèn)提供),但并不強(qiáng)制要求使用。 在大部分用例中,開(kāi)發(fā)者都關(guān)心枚舉的實(shí)際值是什么。 但如果值 確實(shí) 重要,則枚舉可以使用任意的值。
枚舉屬于 Python 的類(lèi),并可具有普通方法和特殊方法。 如果我們有這樣一個(gè)枚舉:
>>> 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
...
那么:
>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'
對(duì)于允許內(nèi)容的規(guī)則如下:以單下劃線(xiàn)開(kāi)頭和結(jié)尾的名稱(chēng)是由枚舉保留而不可使用;在枚舉中定義的所有其他屬性將成為該枚舉的成員,例外項(xiàng)則包括特殊方法成員 (__str__(), __add__() 等),描述符 (方法也屬于描述符) 以及在 _ignore_ 中列出的變量名。
注意:如果你的枚舉定義了 __new__() 和/或 __init__() 那么指定給枚舉成員的任何值都會(huì)被傳入這些方法。 請(qǐng)參閱示例 Planet。
受限的 Enum 子類(lèi)化?
一個(gè)新的 Enum 類(lèi)必須基于一個(gè) Enum 類(lèi),至多一個(gè)實(shí)體數(shù)據(jù)類(lèi)型以及出于實(shí)際需要的任意多個(gè)基于 object 的 mixin 類(lèi)。 這些基類(lèi)的順序?yàn)?
class EnumName([mix-in, ...,] [data-type,] base-enum):
pass
另外,僅當(dāng)一個(gè)枚舉未定義任何成員時(shí)才允許子類(lèi)化該枚舉。 因此禁止以下寫(xiě)法:
>>> class MoreColor(Color):
... PINK = 17
...
Traceback (most recent call last):
...
TypeError: Cannot extend enumerations
但是允許這樣的寫(xiě)法:
>>> class Foo(Enum):
... def some_behavior(self):
... pass
...
>>> class Bar(Foo):
... HAPPY = 1
... SAD = 2
...
允許子類(lèi)化定義了成員的枚舉將會(huì)導(dǎo)致違反類(lèi)型與實(shí)例的某些重要的不可變規(guī)則。 在另一方面,允許在一組枚舉之間共享某些通用行為也是有意義的。 (請(qǐng)參閱示例 OrderedEnum 。)
封存?
枚舉可以被封存與解封:
>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True
封存的常規(guī)限制同樣適用:可封存枚舉必須在模塊的最高層級(jí)中定義,因?yàn)榻夥獠僮饕笏鼈兛梢詮脑撃K導(dǎo)入。
注解
使用 pickle 協(xié)議版本 4 可以方便地封存嵌套在其他類(lèi)中的枚舉。
通過(guò)在枚舉類(lèi)中定義 __reduce_ex__() 可以對(duì) Enum 成員的封存/解封方式進(jìn)行修改。
可用 API?
Enum 類(lèi)屬于可調(diào)用對(duì)象,它提供了以下可用的 API:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> Animal.ANT.value
1
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
該 API 的主義類(lèi)似于 namedtuple。 調(diào)用 Enum 的第一個(gè)參數(shù)是枚舉的名稱(chēng)。
第二個(gè)參數(shù)是枚舉成員名稱(chēng)的 來(lái)源。 它可以是一個(gè)用空格分隔的名稱(chēng)字符串、名稱(chēng)序列、鍵/值對(duì) 2 元組的序列,或者名稱(chēng)到值的映射(例如字典)。 最后兩種選項(xiàng)使得可以為枚舉任意賦值;其他選項(xiàng)會(huì)自動(dòng)以從 1 開(kāi)始遞增的整數(shù)賦值(使用 start 形參可指定不同的起始值)。 返回值是一個(gè)派生自 Enum 的新類(lèi)。 換句話(huà)說(shuō),以上對(duì) Animal 的賦值就等價(jià)于:
>>> class Animal(Enum):
... ANT = 1
... BEE = 2
... CAT = 3
... DOG = 4
...
默認(rèn)以 1 而以 0 作為起始數(shù)值的原因在于 0 的布爾值為 False,但所有枚舉成員都應(yīng)被求值為 True。
封存通過(guò)功能性 API 創(chuàng)建的枚舉可能會(huì)有點(diǎn)麻煩,因?yàn)橐褂脦褩5膶?shí)現(xiàn)細(xì)節(jié)來(lái)嘗試并找出枚舉是在哪個(gè)模塊中創(chuàng)建的(例如,當(dāng)你使用不同模塊的工具函數(shù)時(shí)可能會(huì)失敗,在 IronPython 或 Jython 上也可能會(huì)沒(méi)有效果)。 解決辦法是顯式地指定模塊名稱(chēng),如下所示:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
警告
如果未提供 module,且 Enum 無(wú)法確定是哪個(gè)模塊,新的 Enum 成員將不可被解封;為了讓錯(cuò)誤盡量靠近源頭,封存將被禁用。
新的 pickle 協(xié)議版本 4 在某些情況下同樣依賴(lài)于 __qualname__ 被設(shè)為特定位置以便 pickle 能夠找到相應(yīng)的類(lèi)。 例如,類(lèi)是否存在于全局作用域的 SomeData 類(lèi)中:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')
完整的簽名為:
Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
- 值
將被新 Enum 類(lèi)將記錄為其名稱(chēng)的數(shù)據(jù)。
- names
Enum 的成員。 這可以是一個(gè)空格或逗號(hào)分隔的字符串 (起始值將為 1,除非另行指定):
'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
或是一個(gè)名稱(chēng)的迭代器:
['RED', 'GREEN', 'BLUE']
或是一個(gè) (名稱(chēng), 值) 對(duì)的迭代器:
[('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]
或是一個(gè)映射:
{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
- module
新 Enum 類(lèi)所在模塊的名稱(chēng)。
- qualname
新 Enum 類(lèi)在模塊中的具體位置。
- type
要加入新 Enum 類(lèi)的類(lèi)型。
- start
當(dāng)只傳入名稱(chēng)時(shí)要使用的起始數(shù)值。
在 3.5 版更改: 增加了 start 形參。
派生的枚舉?
IntEnum?
所提供的第一個(gè)變種 Enum 同時(shí)也是 int 的一個(gè)子類(lèi)。 IntEnum 的成員可與整數(shù)進(jìn)行比較;通過(guò)擴(kuò)展,不同類(lèi)型的整數(shù)枚舉也可以相互進(jìn)行比較:
>>> from enum import IntEnum
>>> class Shape(IntEnum):
... CIRCLE = 1
... SQUARE = 2
...
>>> class Request(IntEnum):
... POST = 1
... GET = 2
...
>>> Shape == 1
False
>>> Shape.CIRCLE == 1
True
>>> Shape.CIRCLE == Request.POST
True
不過(guò),它們?nèi)匀徊豢膳c標(biāo)準(zhǔn) Enum 枚舉進(jìn)行比較:
>>> class Shape(IntEnum):
... CIRCLE = 1
... SQUARE = 2
...
>>> class Color(Enum):
... RED = 1
... GREEN = 2
...
>>> Shape.CIRCLE == Color.RED
False
IntEnum 值在其他方面的行為都如你預(yù)期的一樣類(lèi)似于整數(shù):
>>> int(Shape.CIRCLE)
1
>>> ['a', 'b', 'c'][Shape.CIRCLE]
'b'
>>> [i for i in range(Shape.SQUARE)]
[0, 1]
IntFlag?
所提供的下一個(gè) Enum 的變種 IntFlag 同樣是基于 int 的,不同之處在于 IntFlag 成員可使用按位運(yùn)算符 (&, |, ^, ~) 進(jìn)行合并且結(jié)果仍然為 IntFlag 成員。 如果,正如名稱(chēng)所表明的,IntFlag 成員同時(shí)也是 int 的子類(lèi),并能在任何使用 int 的場(chǎng)合被使用。 IntFlag 成員進(jìn)行除按位運(yùn)算以外的其他運(yùn)算都將導(dǎo)致失去 IntFlag 成員資格。
3.6 新版功能.
示例 IntFlag 類(lèi):
>>> from enum import IntFlag
>>> class Perm(IntFlag):
... R = 4
... W = 2
... X = 1
...
>>> Perm.R | Perm.W
<Perm.R|W: 6>
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
>>> Perm.R in RW
True
對(duì)于組合同樣可以進(jìn)行命名:
>>> class Perm(IntFlag):
... R = 4
... W = 2
... X = 1
... RWX = 7
>>> Perm.RWX
<Perm.RWX: 7>
>>> ~Perm.RWX
<Perm.-8: -8>
IntFlag 和 Enum 的另一個(gè)重要區(qū)別在于如果沒(méi)有設(shè)置任何旗標(biāo)(值為 0),則其布爾值為 False:
>>> Perm.R & Perm.X
<Perm.0: 0>
>>> bool(Perm.R & Perm.X)
False
由于 IntFlag 成員同時(shí)也是 int 的子類(lèi),因此它們可以相互組合:
>>> Perm.X | 8
<Perm.8|X: 9>
標(biāo)志?
最后一個(gè)變種是 Flag。 與 IntFlag 類(lèi)似,Flag 成員可使用按位運(yùn)算符 (&, |, ^, ~) 進(jìn)行組合,與 IntFlag 不同的是它們不可與任何其它 Flag 枚舉或 int 進(jìn)行組合或比較。 雖然可以直接指定值,但推薦使用 auto 作為值以便讓 Flag 選擇適當(dāng)?shù)闹怠?/p>
3.6 新版功能.
與 IntFlag 類(lèi)似,如果 Flag 成員的某種組合導(dǎo)致沒(méi)有設(shè)置任何旗標(biāo),則其布爾值為 False:
>>> from enum import Flag, auto
>>> class Color(Flag):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color.0: 0>
>>> bool(Color.RED & Color.GREEN)
False
單個(gè)旗標(biāo)的值應(yīng)當(dāng)為二的乘方 (1, 2, 4, 8, ...),旗標(biāo)的組合則無(wú)此限制:
>>> class Color(Flag):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
... WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
<Color.WHITE: 7>
對(duì) "no flags set" 條件指定一個(gè)名稱(chēng)并不會(huì)改變其布爾值:
>>> class Color(Flag):
... BLACK = 0
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.BLACK
<Color.BLACK: 0>
>>> bool(Color.BLACK)
False
其他事項(xiàng)?
雖然 IntEnum 是 enum 模塊的一部分,但要獨(dú)立實(shí)現(xiàn)也應(yīng)該相當(dāng)容易:
class IntEnum(int, Enum):
pass
這里演示了如何定義類(lèi)似的派生枚舉;例如一個(gè)混合了 str 而不是 int 的 StrEnum。
幾條規(guī)則:
當(dāng)子類(lèi)化
Enum時(shí),在基類(lèi)序列中的混合類(lèi)型必須出現(xiàn)于Enum本身之前,如以上IntEnum的例子所示。雖然
Enum可以擁有任意類(lèi)型的成員,不過(guò)一旦你混合了附加類(lèi)型,則所有成員必須為相應(yīng)類(lèi)型的值,如在上面的例子中即為int。 此限制不適用于僅添加方法而未指定另一數(shù)據(jù)類(lèi)型如int或str的混合類(lèi)。當(dāng)混合了另一數(shù)據(jù)類(lèi)型時(shí),
value屬性會(huì) 不同于 枚舉成員自身,但它們?nèi)员3值葍r(jià)且比較結(jié)果也相等。%-style formatting: %s 和 %r 會(huì)分別調(diào)用
Enum類(lèi)的__str__()和__repr__();其他代碼 (例如表示 IntEnum 的 %i 或 %h) 會(huì)將枚舉成員視為對(duì)應(yīng)的混合類(lèi)型。格式化字符串字面值,
str.format()和format()將使用混合類(lèi)型的__format__()。 如果需要Enum類(lèi)的str()或repr(),請(qǐng)使用 !s 或 !r 格式代碼。
有趣的示例?
雖然 Enum, IntEnum, IntFlag 和 Flag 預(yù)期可覆蓋大多數(shù)應(yīng)用場(chǎng)景,但它們無(wú)法覆蓋全部。 這里有一些不同類(lèi)型枚舉的方案,它們可以被直接使用,或是作為自行創(chuàng)建的參考示例。
省略值?
在許多應(yīng)用場(chǎng)景中人們都不關(guān)心枚舉的實(shí)際值是什么。 有幾個(gè)方式可以定義此種類(lèi)型的簡(jiǎn)單枚舉:
使用以上任何一種方法均可向用戶(hù)指明值并不重要,并且使人能夠添加、移除或重排序成員而不必改變其余成員的數(shù)值。
無(wú)論你選擇何種方法,你都應(yīng)當(dāng)提供一個(gè) repr() 并且它也需要隱藏(不重要的)值:
>>> class NoValue(Enum):
... def __repr__(self):
... return '<%s.%s>' % (self.__class__.__name__, self.name)
...
使用 auto?
使用 auto 看起來(lái)是這樣:
>>> class Color(NoValue):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN>
使用 object?
使用 object 的形式如下:
>>> class Color(NoValue):
... RED = object()
... GREEN = object()
... BLUE = object()
...
>>> Color.GREEN
<Color.GREEN>
使用描述性字符串?
使用字符串作為值的形式如下:
>>> class Color(NoValue):
... RED = 'stop'
... GREEN = 'go'
... BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
'go'
使用自定義的 __new__()?
使用自動(dòng)編號(hào) __new__() 的形式如下:
>>> class AutoNumber(NoValue):
... def __new__(cls):
... value = len(cls.__members__) + 1
... obj = object.__new__(cls)
... obj._value_ = value
... return obj
...
>>> class Color(AutoNumber):
... RED = ()
... GREEN = ()
... BLUE = ()
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
2
OrderedEnum?
一個(gè)有序枚舉,它不是基于 IntEnum,因此保持了正常的 Enum 不變特性(例如不可與其他枚舉進(jìn)行比較):
>>> class OrderedEnum(Enum):
... def __ge__(self, other):
... if self.__class__ is other.__class__:
... return self.value >= other.value
... return NotImplemented
... def __gt__(self, other):
... if self.__class__ is other.__class__:
... return self.value > other.value
... return NotImplemented
... def __le__(self, other):
... if self.__class__ is other.__class__:
... return self.value <= other.value
... return NotImplemented
... def __lt__(self, other):
... if self.__class__ is other.__class__:
... return self.value < other.value
... return NotImplemented
...
>>> class Grade(OrderedEnum):
... A = 5
... B = 4
... C = 3
... D = 2
... F = 1
...
>>> Grade.C < Grade.A
True
DuplicateFreeEnum?
如果發(fā)現(xiàn)重復(fù)的成員名稱(chēng)則將引發(fā)錯(cuò)誤而不是創(chuàng)建別名:
>>> class DuplicateFreeEnum(Enum):
... def __init__(self, *args):
... cls = self.__class__
... if any(self.value == e.value for e in cls):
... a = self.name
... e = cls(self.value).name
... raise ValueError(
... "aliases not allowed in DuplicateFreeEnum: %r --> %r"
... % (a, e))
...
>>> class Color(DuplicateFreeEnum):
... RED = 1
... GREEN = 2
... BLUE = 3
... GRENE = 2
...
Traceback (most recent call last):
...
ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN'
注解
這個(gè)例子適用于子類(lèi)化 Enum 來(lái)添加或改變禁用別名以及其他行為。 如果需要的改變只是禁用別名,也可以選擇使用 unique() 裝飾器。
Planet?
如果定義了 __new__() 或 __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):
... # universal gravitational constant (m3 kg-1 s-2)
... G = 6.67300E-11
... return G * self.mass / (self.radius * self.radius)
...
>>> Planet.EARTH.value
(5.976e+24, 6378140.0)
>>> Planet.EARTH.surface_gravity
9.802652743337129
TimePeriod?
一個(gè)演示如何使用 _ignore_ 屬性的例子:
>>> from datetime import timedelta
>>> class Period(timedelta, Enum):
... "different lengths of time"
... _ignore_ = 'Period i'
... Period = vars()
... for i in range(367):
... Period['day_%d' % i] = i
...
>>> list(Period)[:2]
[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
>>> list(Period)[-2:]
[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]
各種枚舉有何區(qū)別??
枚舉具有自定義的元類(lèi),它會(huì)影響所派生枚舉類(lèi)及其實(shí)例(成員)的各個(gè)方面。
枚舉類(lèi)?
EnumMeta 元類(lèi)負(fù)責(zé)提供 __contains__(), __dir__(), __iter__() 及其他方法以允許用戶(hù)通過(guò) Enum 類(lèi)來(lái)完成一般類(lèi)做不到的事情,例如 list(Color) 或 some_enum_var in Color。 EnumMeta 會(huì)負(fù)責(zé)確保最終 Enum 類(lèi)中的各種其他方法是正確的 (例如 __new__(), __getnewargs__(), __str__() 和 __repr__())。
枚舉成員(即實(shí)例)?
有關(guān)枚舉成員最有趣的特點(diǎn)是它們都是單例對(duì)象。 EnumMeta 會(huì)在創(chuàng)建 Enum 類(lèi)本身時(shí)將它們?nèi)縿?chuàng)建完成,然后準(zhǔn)備好一個(gè)自定義的 __new__(),通過(guò)只返回現(xiàn)有的成員實(shí)例來(lái)確保不會(huì)再實(shí)例化新的對(duì)象。
細(xì)節(jié)要點(diǎn)?
支持 __dunder__ 名稱(chēng)?
__members__ 是一個(gè) OrderedDict,由 member_name:member 條目組成。 它只在類(lèi)上可用。
如果指定了 __new__(),它必須創(chuàng)建并返回枚舉成員;相應(yīng)地設(shè)定成員的 _value_ 也是一個(gè)很好的主意。 一旦所有成員都創(chuàng)建完成它就不會(huì)再被使用。
支持的 _sunder_ 名稱(chēng)?
_name_-- 成員的名稱(chēng)_value_-- 成員的值;可以在__new__中設(shè)置 / 修改_missing_-- 當(dāng)未發(fā)現(xiàn)某個(gè)值時(shí)所使用的查找函數(shù);可被重載_ignore_-- 一個(gè)名稱(chēng)列表,可以為list()或str(),它將不會(huì)被轉(zhuǎn)化為成員,并會(huì)從最終類(lèi)中被移除_order_-- 用于 Python 2/3 代碼以確保成員順序一致(類(lèi)屬性,在類(lèi)創(chuàng)建期間會(huì)被移除)_generate_next_value_-- 用于 Functional API 并通過(guò)auto為枚舉成員獲取適當(dāng)?shù)闹担豢杀恢剌d
3.6 新版功能: _missing_, _order_, _generate_next_value_
3.7 新版功能: _ignore_
用來(lái)幫助 Python 2 / Python 3 代碼保持同步提供 _order_ 屬性。 它將與枚舉的實(shí)際順序進(jìn)行對(duì)照檢查,如果兩者不匹配則會(huì)引發(fā)錯(cuò)誤:
>>> class Color(Enum):
... _order_ = 'RED GREEN BLUE'
... RED = 1
... BLUE = 3
... GREEN = 2
...
Traceback (most recent call last):
...
TypeError: member order does not match _order_
注解
在 Python 2 代碼中 _order_ 屬性是必須的,因?yàn)槎x順序在被記錄之前就會(huì)丟失。
Enum 成員類(lèi)型?
Enum 成員是其 Enum 類(lèi)的實(shí)例,一般通過(guò) EnumClass.member 的形式來(lái)訪(fǎng)問(wèn)。 在特定情況下它們也可通過(guò) EnumClass.member.member 的形式來(lái)訪(fǎng)問(wèn),但你絕對(duì)不應(yīng)這樣做,因?yàn)椴檎铱赡苁。蛘吒愀獾胤祷啬闼檎业?Enum 成員以外的對(duì)象(這也是成員應(yīng)使用全大寫(xiě)名稱(chēng)的另一個(gè)好理由):
>>> class FieldTypes(Enum):
... name = 0
... value = 1
... size = 2
...
>>> FieldTypes.value.size
<FieldTypes.size: 2>
>>> FieldTypes.size.value
2
在 3.5 版更改.
Enum 類(lèi)和成員的布爾值?
混合了非 Enum 類(lèi)型(例如 int, str 等)的 Enum 成員會(huì)按所混合類(lèi)型的規(guī)則被求值;在其他情況下,所有成員都將被求值為 True。 要使你的自定義 Enum 的布爾值取決于成員的值,請(qǐng)?jiān)谀愕念?lèi)中添加以下代碼:
def __bool__(self):
return bool(self.value)
帶有方法的 Enum 類(lèi)?
如果你為你的 Enum 子類(lèi)添加了額外的方法,如同上述的 Planet 類(lèi)一樣,這些方法將在對(duì)成員執(zhí)行 dir() 時(shí)顯示出來(lái),但對(duì)類(lèi)執(zhí)行時(shí)則不會(huì)顯示:
>>> dir(Planet)
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']
組合 Flag 的成員?
如果 Flag 成員的某種組合未被命名,則 repr() 將包含所有已命名的旗標(biāo)和值中所有已命名的旗標(biāo)組合:
>>> class Color(Flag):
... RED = auto()
... GREEN = auto()
... BLUE = auto()
... MAGENTA = RED | BLUE
... YELLOW = RED | GREEN
... CYAN = GREEN | BLUE
...
>>> Color(3) # named combination
<Color.YELLOW: 3>
>>> Color(7) # not named combination
<Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>
