草根學Python(十一)枚舉類

前言

雖然沒多少閱讀,可是還是堅持寫下去。對 Python 感興趣的童鞋可以加入 Python 學習討論微信群喔。可以先加我微信,然後拉進群。本人微信:【androidweb】

目錄

一、枚舉類的使用

實際開發中,我們離不開定義常量,當我們需要定義常量時,其中一個辦法是用大寫變數通過整數來定義,例如月份:

JAN = 1nFEB = 2nMAR = 3n...nNOV = 11nDEC = 12n

當然這樣做簡單快捷,缺點是類型是 int ,並且仍然是變數。

那有沒有什麼好的方法呢?

這時候我們定義一個 class 類型,每個常量都是 class 裡面唯一的實例。正好 Python 提供了 Enum 類來實現這個功能如下:

#!/usr/bin/env python3n# -*- coding: UTF-8 -*-nnfrom enum import EnumnnMonth = Enum(Month, (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec))nn# 遍歷枚舉類型nfor name, member in Month.__members__.items():n print(name, ---------, member, ----------, member.value)nn# 直接引用一個常量nprint(n, Month.Jan)n

輸出的結果如下:

可見,我們可以直接使用 Enum 來定義一個枚舉類。上面的代碼,我們創建了一個有關月份的枚舉類型 Month ,這裡要注意的是構造參數,第一個參數 Month 表示的是該枚舉類的類名,第二個 tuple 參數,表示的是枚舉類的值;當然,枚舉類通過 __members__ 遍歷它的所有成員的方法。注意的一點是 , member.value 是自動賦給成員的 int類型的常量,默認是從 1 開始的。而且 Enum 的成員均為單例(Singleton),並且不可實例化,不可更改

二、Enum 的源碼

通過上面的實例可以知道通過 __members__ 可以遍歷枚舉類的所有成員。那為什麼呢?

我們可以先來大致看看 Enum 的源碼是如何實現的;Enum 在模塊 enum.py 中,先來看看 Enum 類的片段

class Enum(metaclass=EnumMeta):n """Generic enumeration.n Derive from this class to define new enumerations.n """n

可以看到,Enum 是繼承元類 EnumMeta 的;再看看 EnumMeta 的相關片段

class EnumMeta(type):n """Metaclass for Enum"""n @propertyn def __members__(cls):n """Returns a mapping of member name->value.n This mapping lists all enum members, including aliases. Note that thisn is a read-only view of the internal mapping.n """n return MappingProxyType(cls._member_map_)n

首先 __members__ 方法返回的是一個包含一個 Dict 既 Map 的 MappingProxyType,並且通過 @property 將方法 __members__(cls) 的訪問方式改變為了變數的的形式,既可以直接通過 __members__ 來進行訪問了

三、自定義類型的枚舉

但有些時候我們需要控制枚舉的類型,那麼我們可以 Enum 派生出自定義類來滿足這種需要。通過修改上面的例子:

#!/usr/bin/env python3n# -*- coding: UTF-8 -*-nfrom enum import Enum, uniquennEnum(Month, (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec))nnn# @unique 裝飾器可以幫助我們檢查保證沒有重複值n@uniquenclass Month(Enum):n Jan = Januaryn Feb = Februaryn Mar = Marchn Apr = Apriln May = Mayn Jun = Junen Jul = Julyn Aug = Augustn Sep = September n Oct = Octobern Nov = Novembern Dec = Decembernnnif __name__ == __main__:n print(Month.Jan, ----------,n Month.Jan.name, ----------, Month.Jan.value)n for name, member in Month.__members__.items():n print(name, ----------, member, ----------, member.value)n

輸出的結果如下:

通過上面的例子,可以知道枚舉模塊定義了具有迭代 (interator) 和比較(comparison) 功能的枚舉類型。 它可以用來為值創建明確定義的符號,而不是使用具體的整數或字元串。

四、枚舉的比較

因為枚舉成員不是有序的,所以它們只支持通過標識(identity) 和相等性 (equality) 進行比較。下面來看看 == 和 is 的使用:

#!/usr/bin/env python3n# -*- coding: UTF-8 -*-nfrom enum import Enumnnnclass User(Enum):n Twowater = 98n Liangdianshui = 30n Tom = 12nnnTwowater = User.TwowaternLiangdianshui = User.Liangdianshuinnprint(Twowater == Liangdianshui, Twowater == User.Twowater)nprint(Twowater is Liangdianshui, Twowater is User.Twowater)nntry:n print(n.join( + s.name for s in sorted(User)))nexcept TypeError as err:n print( Error : {}.format(err))n

輸出的結果:

False TruenFalse Truen Error : < not="" supported="" between="" instances="" of="" user="" and="" user<="" code="">n

可以看看最後的輸出結果,報了個異常,那是因為大於和小於比較運算符引發 TypeError 異常。也就是 Enum 類的枚舉是不支持大小運算符的比較的。

那麼能不能讓枚舉類進行大小的比較呢?

當然是可以的,使用 IntEnum 類進行枚舉,就支持比較功能。

#!/usr/bin/env python3n# -*- coding: UTF-8 -*-nimport enumnnnclass User(enum.IntEnum):n Twowater = 98n Liangdianshui = 30n Tom = 12nnntry:n print(n.join(s.name for s in sorted(User)))nexcept TypeError as err:n print( Error : {}.format(err))n

看看輸出的結果:

TomnLiangdianshuinTwowatern

通過輸出的結果可以看到,枚舉類的成員通過其值得大小進行了排序。也就是說可以進行大小的比較。


推薦閱讀:

Flask文件上傳(三):完整實現
Python · 樸素貝葉斯(三)· GaussianNB
怎樣才能寫出 Pythonic 的代碼?
vim怎麼匹配多個相同字元並替換成字元加數字遞增的形式?
Python實踐20-閉包簡介

TAG:Python | Python入门 | Python教程 |