連載
» 2022年06月14日 05時00分 公開

[解決!Python]Pythonで列挙型(enum)を使うには解決!Python

enumモジュールのEnumクラスやIntEnumクラス、Flagクラスを継承して、列挙型を定義したり、そのメンバーを扱ったりする方法を紹介する。

[かわさきしんじ,Deep Insider編集部]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「解決!Python」のインデックス

連載目次

from enum import Enum

# 列挙型の定義
class Animal(Enum):
    CAT = 1
    DOG = 2
    COW = 3

# 列挙型のメンバーへのアクセス
print(Animal.CAT)  # Animal.CAT
repr(Animal.CAT)  # '<Animal.CAT: 1>'
print(Animal.DOG.name)  # DOG
print(Animal.COW.value)  # 3

# 列挙型のメンバーの比較
favorit_animal = 1
if favorit_animal == Animal.CAT:  # NG:列挙型のメンバーと整数値は比較できない
    print('meow')
else:
    print('???')
# 出力結果: ???

favorit_animal = 1
favorit_animal = Animal(favorit_animal)  # 指定した値に対応するメンバーを取得
print(favorit_animal)  # Animal.CAT
if favorit_animal == Animal.CAT:  # OK:Animal.CAT == Animal.CAT
    print('meow')
else:
    print('???')
# 出力結果: meow

favorit_animal = 'DOG'
favorit_animal = Animal[favorit_animal]  # 列挙型のメンバーにその名前でアクセス
print(favorit_animal)  # Animal.DOG
if favorit_animal == Animal.DOG:  # OK
    print('bow wow')
else:
    print('???')
# 出力結果: bow wow

# 列挙型のメンバー間に順序関係はない
if Animal.CAT < Animal.DOG:
    print('cat has a higher priority than dog')
# TypeError: '<' not supported between instances of 'Animal' and 'Animal'

# 列挙型のメンバーの列挙
for member in Animal:
    print(member)
# 出力結果:
#Animal.CAT
#Animal.DOG
#Animal.COW

# __members__属性を使ったメンバーの列挙
for name, animal in Animal.__members__.items():
    print(f'name: {name}, member: {animal}')
# 出力結果:
#name: CAT, member: Animal.CAT
#name: DOG, member: Animal.DOG
#name: COW, member: Animal.COW

# 列挙型のメンバーの値を自動的に設定する
from enum import auto

class Animal(Enum):
    CAT = auto()  # デフォルトでは1始まりの値となる
    DOG = auto()
    COW = auto()

for member in Animal:
    repr(member)
# 出力結果:
#'<Animal.CAT: 1>'
#'<Animal.DOG: 2>'
#'<Animal.COW: 3>'

# メンバーに別名を与える
class Animal(Enum):
    CAT = auto()
    DOG = auto()
    COW = auto()
    NYANKO = CAT  # Animal.NYANKOはAnimal.CATの別名

print(Animal.NYANKO)  # Animal.CAT

# メンバーが重複する値を持つことを許さない
from enum import unique

@unique
class Animal(Enum):
    CAT = auto()  # デフォルトでは1始まりの値となる
    DOG = auto()
    COW = auto()
    NYANKO = CAT
# ValueError: duplicate values found in <enum 'Animal'>: NYANKO -> CAT

# 他の列挙型との比較もできない
class PhoneticCode(Enum):
    ALPHA = auto()
    BETA = auto()
    GAMMA = auto()

print(f'{PhoneticCode.ALPHA.value}, {Animal.CAT.value}'# 1, 1
print(PhoneticCode.ALPHA == Animal.CAT)  # False

# 整数値との比較が可能な列挙型
from enum import IntEnum

class PhoneticCode(IntEnum):
    ALPHA = auto()
    BETA = auto()
    GAMMA = auto()

print(PhoneticCode.ALPHA == 1# True
print(PhoneticCode.ALPHA == Animal.CAT)  # False

# フラグ
from enum import Flag

class Color(Flag):
    BLUE = auto()  # 1 = 0b001
    GREEN = auto()  # 2 = 0b010
    RED = auto()  # 4 = 0b100
    CYAN = GREEN | BLUE  # 0b010 | 0b001 = 0b11 = 3
    MAGENTA = RED | BLUE  # 0b100 | 0b001 = 0b101 = 5
    YELLOW = RED | GREEN  # 0b100 | 0b010 = 0b110 = 6
    WHITE = RED | GREEN | BLUE  # 0b100 | 0b010 | 0b001 = 0b111 = 7
    BLACK = RED & GREEN & BLUE  # 0b100 & 0b010 & 0b001 = 0b000 = 0
    AQUA = CYAN
    FUCHSIA = MAGENTA

for c in Color:
    repr(c)
# 出力結果:
#'<Color.BLUE: 1>'
#'<Color.GREEN: 2>'
#'<Color.RED: 4>'
#'<Color.CYAN: 3>'
#'<Color.MAGENTA: 5>'
#'<Color.YELLOW: 6>'
#'<Color.WHITE: 7>'
#'<Color.BLACK: 0>'

# 列挙型にメソッドを持たせる
class Color(Flag):
    BLUE = 0x000FF
    GREEN = 0x00FF00
    RED = 0xFF0000
    CYAN = GREEN | BLUE
    MAGENTA = RED | BLUE
    YELLOW = RED | GREEN
    WHITE = RED | GREEN | BLUE
    BLACK = RED & GREEN & BLUE
    AQUA = CYAN
    FUCHSIA = MAGENTA

    def __repr__(self):
        return f'<Color.{self.name}: {self.value:#08x}>'

for c in Color:
    repr(c)
# 出力結果:
#'<Color.BLUE: 0x0000ff>'
#'<Color.GREEN: 0x00ff00>'
#'<Color.RED: 0xff0000>'
#'<Color.CYAN: 0x00ffff>'
#'<Color.MAGENTA: 0xff00ff>'
#'<Color.YELLOW: 0xffff00>'
#'<Color.WHITE: 0xffffff>'
#'<Color.BLACK: 0x000000>'


列挙型の基本

 Pythonでは言語標準で列挙型(定数として機能する値に名前を付けたものの集まり)を定義することはできない。しかし、Pythonに標準で付属するenumモジュールを使用することで列挙型を定義/使用できるようになる。

 簡単な例を以下に示す。

from enum import Enum

class Animal(Enum):
    CAT = 1
    DOG = 2
    COW = 3


 これはAnimalという列挙型を定義する例だ。enumモジュールのEnumクラスを基底クラスとしてAnimalクラスを定義している。列挙型のメンバーとしてはCAT、DOG、COWの3つがあり、それぞれ1、2、3という値が与えられている(本稿では整数値を列挙型メンバーの値として割り当てているが、実際には任意のオブジェクトを割り当てられる)。

 列挙型のメンバーには次のように「列挙型.メンバー名」としてアクセスできる。

print(Animal.CAT)  # Animal.CAT
repr(Animal.CAT)  # '<Animal.CAT: 1>'


 上の出力結果から、Animal列挙型のCATメンバーの値が1であることが分かるはずだ。

 列挙型のメンバーにはname属性とvalue属性があり、これらを使うことでメンバー名と割り当てられた値を取得できる。

print(Animal.DOG.name)  # DOG
print(Animal.COW.value)  # 3


列挙型のメンバーの比較

 列挙型のメンバーはif文やmatch文で何かの値と比較して、メンバーのどれが指定されているかによって、処理を振り分けるといった処理をするために使うことが多いはずだ。

 以下に例を示す。

favorit_animal = 1
if favorit_animal == Animal.CAT:  # NG:列挙型のメンバーと整数値は比較できない
    print('meow')
else:
    print('???')
# 出力結果: ???


 しかし、上の例では整数値1とAnimcal.CAT(その値は1)との比較が失敗して「???」と表示される。このようにEnumクラスから派生した列挙型は整数値と比較できないようになっている。そこで、列挙型以外の値と列挙型のメンバーとを比較する際には、比較対象の値(例えば、input関数の戻り値を整数化した値など)に対応する列挙型の値を取得する必要がある。

 以下に例を示す。

favorit_animal = 1
favorit_animal = Animal(favorit_animal)  # 指定した値に対応するメンバーを取得
print(favorit_animal)  # Animal.CAT
if favorit_animal == Animal.CAT:  # OK:Animal.CAT == Animal.CAT
    print('meow')
else:
    print('???')
# 出力結果: meow


 この例では「Animal(favorit_animal)」として、変数favorit_animalの値(ここでは1)に対応したメンバー(Animal.CAT)を取得し、それを用いて、比較を行うようにしている。この結果、猫の鳴き声である「meow」が表示されるようになった。

 あるいは、列挙型のメンバーの名前を使って以下のようにインデックスアクセスを行い、対応するメンバーを取得することも可能だ。

favorit_animal = 'DOG'
favorit_animal = Animal[favorit_animal]  # 列挙型のメンバーにその名前でアクセス
print(favorit_animal)  # Animal.DOG
if favorit_animal == Animal.DOG:  # OK
    print('bow wow')
else:
    print('???')
# 出力結果: bow wow


 この例では「Animal[favorit_animal]」として、変数favorit_animalの値(ここでは'DOG')に対応したメンバー(Animal.DOG)を取得し、それを用いて、比較を行うようにしている。この結果、犬の鳴き声である「bow wow」が表示されるようになった。

 なお、列挙型のメンバーの間には順序関係はないので、次のようなコードは書けない。

# 列挙型のメンバー間に順序関係はない
if Animal.CAT < Animal.DOG:
    print('cat has a higher priority than dog')
# TypeError: '<' not supported between instances of 'Animal' and 'Animal'


列挙型のメンバーの列挙

 列挙型のメンバーは次のようにして反復的に取り出せる。取り出される順序は、メンバーを定義した順序となる。

for member in Animal:
    print(member)
# 出力結果:
#Animal.CAT
#Animal.DOG
#Animal.COW


 列挙型の__members__属性にはメンバーの名前と対応するメンバーが含まれているので、この属性を使ってもよい。

for name, animal in Animal.__members__.items():
    print(f'name: {name}, member: {animal}')
# 出力結果:
#name: CAT, member: Animal.CAT
#name: DOG, member: Animal.DOG
#name: COW, member: Animal.COW


列挙型のメンバーの値を自動的に設定する

 冒頭のコードでは「メンバー名 = 値」としていたが、メンバーが特定の値である必要がなければ、enumモジュールのautoクラスを用いてメンバーに割り当てる値を自動生成してもよい。デフォルトでは1から始まる整数値が順次割り当てられる。

 以下に例を示す。

from enum import auto

class Animal(Enum):
    CAT = auto()  # デフォルトでは1始まりの値となる
    DOG = auto()
    COW = auto()

for member in Animal:
    repr(member)
# 出力結果:
#'<Animal.CAT: 1>'
#'<Animal.DOG: 2>'
#'<Animal.COW: 3>'


 この例では3つのメンバーを定義するのにautoクラスを使っている。これにより、各メンバーにどんな値が割り当てられたかをfor文で調べているが、順番に1、2、3が割り当てられていることが分かる。

メンバーに別名を与える

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。