URLhttps://learnscript.net/zh-hant/python/enumerations/flags/
    複製連結移至說明  範例

    如何定義和使用支援成員組合的列舉?Python 旗標介紹

    閱讀 9:27·字數 2839·發佈 
    Youtube 頻道
    訂閱 480

    先決條件

    閱讀本節的先決條件是對 Python 列舉以及列舉的概念有所掌握,你可以檢視Python 列舉介紹,以及定義和使用列舉程式設計教學列舉介紹,以及列舉成員的組合,判斷,移除來了解相關資訊。

    Python 旗標

    Python 旗標(Flag)是一種特殊的列舉,他允許多個旗標(列舉)成員之間的組合操作,其組合的結果依然是一個旗標(列舉)成員。

    要在 Python 中使用旗標,你需要定義從enum模組的FlagIntFlag繼承的衍生類別。

    旗標成員的取值範圍是什麽?

    在正常情況下,旗標成員的值應該是二的整數次冪2ⁿn大於等於0),使用enum模組的auto類別,可以輕松實作對2ⁿ的自動計算。旗標成員也可以是2ⁿ以外的值,但他必須表示其他旗標成員的某種有效組合,比如,0表示空的組合(不包含任何其他成員)。

    如果一個旗標成員的取值不在以上範圍之內,那麽該成員在一些操作中可能不會發揮作用。

    什麽是旗標的正規化旗標成員?

    如上所述,Python 旗標中值為二的整數次冪2ⁿn大於等於0)的成員,被稱為旗標的正規化旗標成員。

    在下面的旗標COLOR中,我們定義了成員BLACK_AND_WHITE,他是成員WHITEBLACK的組合,其值不是二的整數次冪。

    flags.py
    from enum import *
    
    # auto 會傳回遞增的二的整數次冪 class COLOR(Flag): NONE = 0 WHITE = auto() BLACK = auto() # 因為 WHITE 為 1,BLACK 為 2,因此 3 是 WHITE 與 BLACK 的組合 BLACK_AND_WHITE = 3 RED = auto() BLUE = auto() GREEN = auto()
    bw = COLOR.WHITE | COLOR.BLACK # 顯示為 BLACK_AND_WHITE print(f'{bw.name}={bw.value}')
    BLACK_AND_WHITE=3

    非正規化旗標成員不會出現在對旗標的疊代周遊中

    對一個旗標進行疊代周遊,你將得到該旗標的所有正規化成員,至於已定義的非正規化成員則不會出現。

    flags.py
    # 這裏不會出現 NONE 和 BLACK_AND_WHITE
    for i in COLOR:
    	print(f'{i.name}={i.value}')
    WHITE=1
    BLACK=2
    RED=4
    BLUE=8
    GREEN=16

    使用 @verify 修飾詞確保旗標成員值的有效性

    使用enum模組的NAMED_FLAGS@verify修飾詞,可以確保旗標成員值的有效性,比如,定義一個不存在的組合是不可行的。

    在使用NAMED_FLAGS之後,旗標DAY的成員THURSDAY將引發例外狀況ValueError,因為他不是二的整數次冪,也不是已定義成員的某種組合。

    flags.py
    @verify(NAMED_FLAGS)
    class DAY(Flag):
    	MONDAY = 1
    	TUESDAY = 2
    	WEDNESDAY = 4
    	# ERROR 9 不是二的整數次冪,也不是 1,2,4 的某種組合
    	THURSDAY = 9
    ValueError: invalid Flag 'DAY': alias THURSDAY is missing value 0x8 [use enum.show_flag_values(value) for details]

    如何取得旗標成員組合中所有的二的整數次冪?

    使用enum模組的show_flag_values函式,可以取得某個旗標成員組合中所有的二的整數次冪2ⁿn大於等於0),結果將作為 Python 串列物件傳回。

    show_flag_values(value)

    value 參數

    value參數是旗標成員的組合。

    flags.py
    import enum
    print(enum.show_flag_values(COLOR.BLACK_AND_WHITE))
    [1, 2]

    取得 Python 旗標中正規化旗標成員的個數

    FlagIntFlag繼承的旗標,可以使用函式len取得正規化成員的個數。

    旗標COLOR的成員NONE的值為0,成員BLACK_AND_WHITE的值為3,因此他們不會被計算。

    flags.py
    # COLOR 中的 NONE 和 BLACK_AND_WHITE 不會被計算
    print(f'COLOR 正規化成員個數:{len(COLOR)}')
    COLOR 正規化成員個數:5

    組合 Python 旗標成員

    FlagIntFlag繼承的旗標,可以使用或位元運算子|,來組合旗標中的多個旗標(列舉)成員。

    flags.py
    # 組合成員 WHITE,BLACK,RED
    color = COLOR.WHITE | COLOR.BLACK | COLOR.RED

    疊代周遊 Python 旗標成員組合中的正規化旗標成員

    通過for陳述式,你可以周遊旗標成員組合中所包含的正規化成員,非正規化成員不會出現在迴圈中。

    flags.py
    # 並不會顯示非正規化成員 BLACK_AND_WHITE
    for i in COLOR.BLACK_AND_WHITE | COLOR.GREEN:
    	print(i)
    COLOR.WHITE
    COLOR.BLACK
    COLOR.GREEN

    判斷 Python 旗標成員是否包含在旗標成員組合中

    FlagIntFlag繼承的旗標,可以使用in運算子或與位元運算子&,來判斷組合中是否包含指定的一個或多個旗標(列舉)成員。

    flags.py
    print(f'color 包含 GREEN?{COLOR.GREEN in color}')
    print(f'color 包含 RED?{(COLOR.RED & color) == COLOR.RED}')
    print(f'color 包含 BLACK_AND_WHITE?{COLOR.BLACK_AND_WHITE in color}')
    color 包含 GREEN?False
    color 包含 RED?True
    color 包含 BLACK_AND_WHITE?True

    取得 Python 旗標成員組合中正規化旗標成員的個數

    對於某個旗標成員組合,函式len可以傳回該組合中正規化旗標成員的個數。

    flags.py
    # color 是 WHITE,BLACK,RED 的組合
    print(f'color 組合中正規化成員的個數為:{len(color)}')
    color 組合中正規化成員的個數為:3

    判斷 Python 旗標成員組合是否為空

    對於某個旗標成員組合,bool類別的建構子可以判斷該組合是否含有任意正規化旗標成員,如果bool傳回False,則說明組合是空的。

    當然,通過len函式並判斷結果是否為0,可以達到相同的效果。

    flags.py
    # color 是 WHITE,BLACK,RED 的組合
    print(f'color 組合是否為空?{not bool(color)}')
    print(f'NONE 是否為空?{not bool(COLOR.NONE)}')
    color 組合是否為空?False
    NONE 是否為空?True

    移除 Python 旗標成員組合中的旗標成員

    FlagIntFlag繼承的旗標,可以使用互斥或位元運算子^,將一個或多個旗標成員,從旗標成員組合中移除。如果需要移除的旗標成員在組合中不存在,那麽該旗標成員將被新增。

    flags.py
    # 從 color 中移除 WHITE 和 BLACK
    print(color ^ COLOR.BLACK_AND_WHITE)
    # 由於 color 不包含 GREEN,因此 GREEN 將被新增
    print(color ^ COLOR.GREEN)
    COLOR.RED
    COLOR.WHITE|BLACK|RED|GREEN

    取得相反的 Python 旗標成員

    FlagIntFlag繼承的旗標,可以使用反相位元運算子~,來取得除指定旗標成員之外的所有正規化旗標成員,如果找不到符合要求的正規化成員,那麽將傳回值為0的成員。

    旗標的反相運算結果僅包含已定義的正規化旗標成員

    與其他程式設計語言不同,旗標的反相運算結果,僅包含已定義的正規化旗標成員,不會包含任何未定義的旗標成員。

    flags.py
    # 取得 WHITE,BLACK,RED 以外的成員
    print(~color)
    # 取得其他所有成員
    print(~COLOR.NONE)
    COLOR.BLUE|GREEN
    COLOR.WHITE|BLACK|RED|BLUE|GREEN

    超出取值範圍的 Python 旗標成員

    每一個旗標成員都是一個旗標(列舉)的執行個體,如果在建立執行個體的過程中,指定了超出範圍的值,比如,包含了不存在的正規化旗標成員,那麽 Python 需要進行相應的處理。

    你可以在定義旗標時,指定boundary參數,該參數將決定如何處理超出取值範圍的旗標成員,他是enum模組的以下變數之一。

    STRICT 變數

    STRICT變數表示,如果旗標成員的取值超出範圍,那麽將引發例外狀況,這是Flag的預設設定。

    CONFORM 變數

    CONFORM變數表示,如果旗標成員的取值超出範圍,那麽移除值所包含的無效成員,從而獲得一個有效的新值。

    EJECT 變數

    EJECT變數表示,如果旗標成員的取值超出範圍,那麽該成員將被轉換為一個整數。

    KEEP 變數

    KEEP變數表示,如果旗標成員的取值超出範圍,那麽保留值所包含的無效成員,這是IntFlag的預設設定。

    對於旗標CONFORMCOLOR,取值5將導致值為4的成員被移除,最終的結果為WHITE。對於旗標EJECTCOLOR,取值5將導致最終計算結果為整數5,而不是旗標成員。對於旗標KEEPCOLOR,取值5將導致未定義的值為4的成員被保留。對於旗標STRICTCOLOR,取值3是沒有問題的,他表示WHITEBLACK的組合,取值5將導致例外狀況ValueError,因為他表示WHITE與值為4的成員的組合,而該成員並未定義。

    flags.py
    class CONFORMCOLOR(Flag, boundary=CONFORM):
    	WHITE = auto()
    	BLACK = auto()
    # 值為 4 的成員將被移除
    print(CONFORMCOLOR(5))
    
    class EJECTCOLOR(Flag, boundary=EJECT): WHITE = auto() BLACK = auto() # 結果為整數 5,不再是旗標成員 print(EJECTCOLOR(5))
    class KEEPCOLOR(Flag, boundary=KEEP): WHITE = auto() BLACK = auto() # 4 將被保留 print(KEEPCOLOR(5))
    class STRICTCOLOR(Flag, boundary=STRICT): WHITE = auto() BLACK = auto() print(STRICTCOLOR(3)) # ERROR 值為 4 的成員並未定義 print(STRICTCOLOR(5))
    CONFORMCOLOR.WHITE
    5
    KEEPCOLOR.WHITE|4
    STRICTCOLOR.WHITE|BLACK

    ValueError: invalid value 5

    繼承 Python 旗標

    在模組enum中,IntFlag是旗標類別Flag的衍生類別,IntFlag基底類別包括Flagint,因此IntFlag可以與其他數值進行算術運算,有效比較,或出現在任何允許使用整數的位置。當參與算術運算時,運算結果將不再是旗標成員。

    flags.py
    class INTCOLOR(IntFlag):
    	WHITE = auto()
    	BLACK = auto()
    
    # 參與比較 print(INTCOLOR.WHITE > 0) # 參與算術運算,結果將不再是一個旗標成員 print(type(INTCOLOR.WHITE + INTCOLOR.BLACK))
    True
    <class 'int'>

    原始碼

    flags.py·codebeatme/python·GitHub