2021-04-03に投稿

Python 標準ライブラリ enum 列挙型

列挙型enumを使うと、カテゴリーに整数や文字列等の識別子を付けて適切に区別できるようになる。

enumの宣言

クラスEnumを継承しメンバーを定義することで列挙型を宣言できる。

from enum import Enum

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

Animal # => <enum 'Animal'>
list(Animal) # =>[<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.SNAKE: 3>]    

他にEnumに空白区切りの文字列、リスト、辞書、タプルのリスト等を引数で渡すことでも列挙を定義できる。

MARK = Enum('MARK', 'SPADE HEART DIAMOND CLUB')
MARK = Enum('MARK', ['SPADE', 'HEART', 'DIAMOND', 'CLUB' ])
MARK = Enum('MARK', {'SPADE':1, 'HEART':2, 'DIAMOND':3, 'CLUB':4 })
MARK = Enum('MARK', [('SPADE',1), ('HEART',2), ('DIAMOND',3), ('CLUB',4) ])

MARK # => <enum 'MARK'>
list(MARK) # => [<MARK.SPADE: 1>, <MARK.HEART: 2>, <MARK.DIAMOND: 3>, <MARK.CLUB: 4>]

enumの使い方

列挙の型.メンバーの名称列挙の型(インデックス)列挙の型[メンバーの名称]
列挙型各要素にアクセスできる。

Animal(1) # => <Animal.DOG: 1>
Animal.CAT # => <Animal.CAT: 2>
Animal['SNAKE'] # = > <Animal.SNAKE: 3>

printやreprで列挙の情報を表示できる。

print(Animal.DOG) # => Animal.DOG
repr(Animal.CAT) # => '<Animal.CAT: 2>'

列挙はイテレートできる。

for a in Animal:
    print(a)

list(Animal) # => [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.SNAKE: 3>]

[a.name for a in Animal] # => ['DOG', 'CAT', 'SNAKE']
[key for key in Animal.__members__.keys()] # => ['DOG', 'CAT', 'SNAKE']
[a.value for a in Animal] # => [1, 2, 3]
[value for value in Animal.__members__.values()] # => [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.SNAKE: 3>]

[(name, value) for name, value in Animal.__members__.items()]
# =&gt; [('DOG', &lt;Animal.DOG: 1&gt;), ('CAT', &lt;Animal.CAT: 2&gt;), ('SNAKE', &lt;Animal.SNAKE: 3&gt;)]

列挙は定義した型、および、Enumのサブクラスになっている。

type(Animal.SNAKE) # => <enum 'Animal'>
isinstance(Animal.DOG, Enum) # => True
isinstance(Animal.DOG, Animal) # => True
issubclass(type(Animal.DOG), Animal) # => True
issubclass(type(Animal.DOG), Enum) # => True

名前、値にはそれぞれnamevalueでアクセスできる。

Animal.CAT.name # => 'CAT'
Animal.CAT.value # => 2

列挙同士は比較可能、整数値との比較はFalseになる。

Animal.DOG == Animal.DOG # => True
Animal.DOG != Animal.DOG # => False
Animal.DOG == Animal.CAT # => False
Animal.DOG != Animal.CAT # => True

Animal.DOG is Animal.DOG # => True
Animal.DOG is not Animal.DOG # => False
Animal.DOG is Animal.CAT # => False
Animal.DOG is not Animal.CAT # => True

Animal.DOG == 1 # => False

一意を保証する列挙

Enumは同じ値のメンバーを定義してもそのままではエラーとならない。
アノテーション@uniqueで一意でない定義を行うとエラーとする事ができる。

from enum import Enum, unique

class EnumSample(Enum):
    AAA = 1
    BBB = 2
    CCC = 2

@unique
class EnumSample(Enum):
    AAA = 1
    BBB = 2
    CCC = 2
# =&gt; ValueError: duplicate values found in &lt;enum 'EnumSample'&gt;: CCC -&gt; BBB

自動採番auto

autoを使うと自動で値の採番を行う事ができる。

from enum import Enum, auto

class EnumSample(Enum):
    AAA = auto()
    BBB = auto()
    CCC = auto()

list(EnumSample) # => [<EnumSample.AAA: 1>, <EnumSample.BBB: 2>, <EnumSample.CCC: 3>]

整数を値にもつ列挙 IntEnum

整数を値に持ち、整数との直接比較ができる列挙型としてIntEnumが提供されている。

from enum import IntEnum

class Animal(IntEnum):
    DOG = 1
    CAT = 2
    SNAKE = 3

Animal.DOG == 1 # => True
Animal.DOG != 2 # => True

int(Animal.SNAKE) # = > 3

整数フラグの列挙 IntFlag

整数の値もったフラグ用途の列挙としてIntFlagが提供されている。

値を各桁のビットで表現し、ビット演算|で値を定義するとOR(または)の意味で使うことができる。

from enum import IntFlag

class Days(IntFlag):
    MONDAY    = 0b0000001
    TUESDAY   = 0b0000010
    WEDNESDAY = 0b0000100
    THURSDAY  = 0b0001000
    FRIDAY    = 0b0010000
    SATURDAY  = 0b0100000
    SUNDAY    = 0b1000000
    WEEKEND   = 0b1100000

monday_or_tuesday = Days.MONDAY | Days.TUESDAY
monday_or_tuesday # => <Days.TUESDAY|MONDAY: 3>

monday_or_tuesday = Days.MONDAY + Days.TUESDAY
monday_or_tuesday # => <Days.TUESDAY|MONDAY: 3>

bool(Days.MONDAY & monday_or_tuesday) # => True
bool(Days.WEDNESDAY & monday_or_tuesday) # => False
bool(Days.SUNDAY & Days.WEEKEND) # => True

Flagの列挙 Flag

Flag用途の列挙としてFlagが提供されている。
autoを組み合わせる事で内部の値を意識しないでフラグとして使える列挙を定義できる。

from enum import Flag, auto

class Days(Flag):
    MONDAY    = auto()
    TUESDAY   = auto()
    WEDNESDAY = auto()
    THURSDAY  = auto()
    FRIDAY    = auto()
    SATURDAY  = auto()
    SUNDAY    = auto()
    WEEKEND   = SUNDAY | SUNDAY

monday_or_tuesday = Days.MONDAY | Days.TUESDAY
monday_or_tuesday # => <Days.TUESDAY|MONDAY: 3>

bool(Days.MONDAY & monday_or_tuesday) # => True
bool(Days.WEDNESDAY & monday_or_tuesday) # => False
bool(Days.SUNDAY & Days.WEEKEND) # => True

文字列を値に持った列挙

列挙の値は数値に限定されない。文字列を値として定義することもできる。

class Animal(Enum):
    DOG = 'd'
    CAT = 'c'
    SNAKE = 's'

Animal.DOG # => <Animal.DOG: 'd'>
Animal['DOG'] # => <Animal.DOG: 'd'>
Animal('d') # => <Animal.DOG: 'd'>
Animal.CAT.value # => 'c'
Animal.SNAKE.name # => 'SNAKE'

自動インクリメントの列挙

__new__で列挙の値が新たに定義されるときの挙動を指定できる。
メンバーの数で値valueを振る事で、値がインクリメントする列挙を定義できる。

class AutoNumber(Enum):
    def __new__(cls, *args):
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)
        obj._value_ = value
        return obj

class AutoSequential(AutoNumber):
    AAA = ()
    BBB = ()
    CCC = ()

list(AutoSequential) # => [<AutoSequential.AAA: 1>, <AutoSequential.BBB: 2>, <AutoSequential.CCC: 3>]

独自のメンバーを持った列挙

__init__で普通のクラスと同じように初期化時の設定を行える。

class Department(Enum):
    ACCOUNTING = (10, '経理')
    GENERALAFFAIRS = (20, '総務')
    Sales1 = (31, '営業1部')
    Sales2 = (32, '営業2部')
    def __init__(self, id, department_name):
        self.id = id
        self.department_name = department_name

Department.ACCOUNTING # => <Department.ACCOUNTING: (10, '経理')>
Department.ACCOUNTING.name # => 'ACCOUNTING'
Department.ACCOUNTING.id # => 10
Department.ACCOUNTING.department_name # => '経理'
Originally published at marusankakusikaku.jp
ツイッターでシェア
みんなに共有、忘れないようにメモ

maru3kaku4kaku

Pythonこつこつ学習中。よく忘れる。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント