列挙型 HOWTO¶
Enum は、ユニークな値に束縛されたシンボル名の集合です。グローバル変数に似ていますが、 repr() がより便利だったり、グルーピングの機能、型安全などいくつかの機能があります。
これらは、限られた選択肢の値の一つを取る変数がある場合に便利です。例えば、曜日情報があります:
>>> from enum import Enum
>>> class Weekday(Enum):
... MONDAY = 1
... TUESDAY = 2
... WEDNESDAY = 3
... THURSDAY = 4
... FRIDAY = 5
... SATURDAY = 6
... SUNDAY = 7
あるいは、RGB三原色でも構いません:
>>> from enum import Enum
>>> class Color(Enum):
... RED = 1
... GREEN = 2
... BLUE = 3
ご覧の通り、 Enum の作成は Enum 自体を継承するクラスを作成するのと同じくらい簡単です。
注釈
Enumメンバーは大文字/小文字?
列挙型は定数を表すために使われるため、また mixin クラスのメソッドや属性との名前の衝突の問題を回避するため、メンバには UPPER_CASE の名前を使うことが強く推奨されており、例でもこのスタイルを用います。
列挙型の性質によって、メンバの値は重要な場合とそうでない場合がありますが、いずれの場合でも、その値は対応するメンバを取得するのに使えます:
>>> Weekday(3)
<Weekday.WEDNESDAY: 3>
ご覧の通り、メンバの repr() は列挙型の名前、メンバの名前、そして値を表示します。メンバの str() は列挙型の名前とメンバの名前のみを表示します。
>>> print(Weekday.THURSDAY)
Weekday.THURSDAY
列挙型のメンバの型はそのメンバの属する列挙型です:
>>> type(Weekday.MONDAY)
<enum 'Weekday'>
>>> isinstance(Weekday.FRIDAY, Weekday)
True
列挙型のメンバーには、その名前だけを保持する name 属性があります。
>>> print(Weekday.TUESDAY.name)
TUESDAY
同様に、値を保持する value 属性もあります。
>>> Weekday.WEDNESDAY.value
3
多くの言語では列挙型を名前と値の組だけとして扱いますが、Python の列挙型には動作を追加できます。
例えば、datetime.date には曜日を返す weekday() と isoweekday() という 2 つのメソッドがあります。
違いは、一方が 0~6、もう一方が 1~7 で数えることです。
この違いを自分で管理する代わりに、Weekday 列挙型にメソッドを追加し、date インスタンスから曜日を取り出して、対応する列挙型メンバーを返すようにできます。
@classmethod
def from_date(cls, date):
return cls(date.isoweekday())
Weekday 列挙型全体は次のようになります。
>>> class Weekday(Enum):
... MONDAY = 1
... TUESDAY = 2
... WEDNESDAY = 3
... THURSDAY = 4
... FRIDAY = 5
... SATURDAY = 6
... SUNDAY = 7
... #
... @classmethod
... def from_date(cls, date):
... return cls(date.isoweekday())
さて、これで今日が何曜日か調べることができます! 見てみましょう:
>>> import datetime as dt
>>> Weekday.from_date(dt.date.today())
<Weekday.TUESDAY: 2>
もちろん、あなたがこれを読んでいるのが他の曜日ならば、その曜日が代わりに表示されます。
この Weekday 列挙型は、変数が 1 つの曜日だけを表せばよい場合には便利ですが、複数の曜日が必要ならどうでしょうか?
例えば、1 週間の家事を割り当てる関数を書いていて、list は使いたくないとします。
別の種類の Enum を使う方法があります。
>>> from enum import Flag
>>> class Weekday(Flag):
... MONDAY = 1
... TUESDAY = 2
... WEDNESDAY = 4
... THURSDAY = 8
... FRIDAY = 16
... SATURDAY = 32
... SUNDAY = 64
ここでは2つの変更が行われています。Flag を継承している点と、値がすべて2の累乗である点です。
先ほどの Weekday 列挙型と同じように、1 つの曜日を選べます。
>>> first_week_day = Weekday.MONDAY
>>> first_week_day
<Weekday.MONDAY: 1>
ただし、Flag は複数のメンバーをひとつの変数にまとめることもできます:
>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
>>> weekend
<Weekday.SATURDAY|SUNDAY: 96>
Flag 変数は反復することもできます:
>>> for day in weekend:
... print(day)
Weekday.SATURDAY
Weekday.SUNDAY
さて、いくつかの家事を設定してみましょう:
>>> chores_for_ethan = {
... 'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY,
... 'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY,
... 'answer SO questions': Weekday.SATURDAY,
... }
指定された日の家事を表示する関数も作成します:
>>> def show_chores(chores, day):
... for chore, days in chores.items():
... if day in days:
... print(chore)
...
>>> show_chores(chores_for_ethan, Weekday.SATURDAY)
answer SO questions
メンバーの実際の値が重要でない場合は、auto() を使用することで手間を省くことができます:
>>> from enum import auto
>>> class Weekday(Flag):
... MONDAY = auto()
... TUESDAY = auto()
... WEDNESDAY = auto()
... THURSDAY = auto()
... FRIDAY = auto()
... SATURDAY = auto()
... SUNDAY = auto()
... WEEKEND = SATURDAY | SUNDAY
列挙型メンバーおよびそれらの属性へのプログラム的アクセス¶
列挙型のメンバーに、プログラムで決めた値を使ってアクセスしたい場合があります。
例えば、プログラムを書く時点では具体的な色が決まっておらず、Color.RED と書けない場合です。
Enum はそのようなアクセスもできます。
>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>
列挙型メンバーに 名前 でアクセスしたい場合はアイテムとしてアクセスできます:
>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>
列挙型メンバーから name や value を取得するには、次のようにします。
>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1
列挙型メンバーと値の重複¶
同じ名前の列挙型メンバーを複数持つことはできません:
>>> class Shape(Enum):
... SQUARE = 2
... SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: 'SQUARE' already defined as 2
しかし、列挙型メンバー は、別の名前を持つことができます。 同じ値を持つ A と B が与えられた場合(そして A が先に定義されている場合)、 B はメンバー A に対するエイリアスとなります。 A の値での検索では、メンバー A が返されます。 A の名前での検索ではメンバー A を返します。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>
注釈
すでに定義されている属性と同じ名前のメンバー (一方がメンバーでもう一方がメソッド、など) の作成、あるいはメンバーと同じ名前の属性の作成はできません。
列挙型の値が一意であることを保証する¶
デフォルトでは、列挙型は同じ値に対する別名として複数の名前を許容します。
この動作が望ましくない場合は、@unique デコレータを使えます。
>>> 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
値の自動設定を使う¶
正確な値が重要でない場合、 auto が使えます:
>>> from enum import Enum, auto
>>> class Color(Enum):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> [member.value for member in Color]
[1, 2, 3]
値は _generate_next_value_() によって選ばれます。
この関数はオーバーライドできます。
>>> class AutoName(Enum):
... @staticmethod
... def _generate_next_value_(name, start, count, last_values):
... return name
...
>>> class Ordinal(AutoName):
... NORTH = auto()
... SOUTH = auto()
... EAST = auto()
... WEST = auto()
...
>>> [member.value for member in Ordinal]
['NORTH', 'SOUTH', 'EAST', 'WEST']
注釈
_generate_next_value_() メソッドは、すべてのメンバーより前に定義しなければなりません。
イテレーション¶
列挙型のメンバーのイテレートは別名をサポートしていません:
>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
>>> list(Weekday)
[<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]
エイリアスである Shape.ALIAS_FOR_SQUARE と Weekday.WEEKEND が表示されていないことに注意してください。
特殊属性 __members__ は読み出し専用で、順序を保持した、対応する名前と列挙型メンバーのマッピングです。これには別名も含め、列挙されたすべての名前が入っています。
>>> 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__ は列挙型メンバーへの詳細なアクセスに使用できます。以下はすべての別名を探す例です:
>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']
注釈
フラグのエイリアスには、 複数のフラグが設定された値(例えば 3)や、 フラグが設定されていない値(例えば 0)が含まれます。
比較¶
列挙型メンバーは同一性を比較できます:
>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True
列挙型の値の順序の比較はサポートされて いません。Enum メンバーは整数ではありません (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
列挙型でない値との等価比較は、常に False を返します。
ただし、IntEnum は明示的に異なる動作をするよう設計されています。
詳しくは後で説明します。
>>> Color.BLUE == 2
False
警告
モジュールは再読み込みすることが可能です。再読み込みされたモジュールに列挙型が含まれている場合、それらは再作成され、新しいメンバーは元のメンバーと同一でない/等しくない可能性があります。
列挙型で許されるメンバーと属性¶
これまでのほとんどの例では、列挙型の値に整数を使用しています。 整数を使うのは短くて便利(そして、 関数 API ではデフォルトで設定される)ですが、これは強制されているわけではありません。大半の使用例では、列挙値の実際の値が何であるかは意識しません。しかし、値が重要な場合、列挙型は任意の値を持つことができます。
列挙型は Python のクラスであり、通常どおりメソッドや特殊メソッドを持つことができます:
>>> class Mood(Enum):
... FUNKY = 1
... HAPPY = 3
...
... def describe(self):
... # ここでは self はメンバー
... return self.name, self.value
...
... def __str__(self):
... return 'my custom str! {0}'.format(self.value)
...
... @classmethod
... def favorite_mood(cls):
... # ここでは cls は列挙型
... return cls.HAPPY
...
上記の結果が以下のようになります:
>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'
使用できるものの規則は次のとおりです。
先頭と末尾がそれぞれ 1 つのアンダースコアである名前は enum が予約しているため使えません。
それ以外の、列挙型の中で定義した属性はすべて列挙型のメンバーになります。
ただし、__str__() や __add__() などの特殊メソッド、デスクリプタ (メソッドもデスクリプタです)、_ignore_ に列挙した変数名は例外です。
注意: 列挙型に __new__() や __init__() を定義すると、メンバーに指定した値がそれらのメソッドに渡されます。
例は Planet を参照してください。
注釈
__new__() を定義した場合、そのメソッドは Enum メンバーの作成時に使われます。
その後は Enum の __new__() に置き換えられ、クラス作成後の既存メンバーの検索に使われます。
詳しくは __new__() と __init__() の使い分け を参照してください。
Enumのサブクラス化の制限¶
新しい Enum クラスには、基底となる列挙型クラスが 1 つ必要です。
加えて、具象データ型を最大 1 つ、object を基底とするミックスインクラスを必要なだけ指定できます。
これらの基底クラスは次の順に並べます。
class EnumName([mix-in, ...,] [data-type,] base-enum):
pass
列挙型のサブクラスの作成はその列挙型にメンバーが一つも定義されていない場合のみ行なえます。従って以下は許されません:
>>> class MoreColor(Color):
... PINK = 17
...
Traceback (most recent call last):
...
TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>
以下のような場合は許されます:
>>> class Foo(Enum):
... def some_behavior(self):
... pass
...
>>> class Bar(Foo):
... HAPPY = 1
... SAD = 2
...
メンバーを定義した列挙型のサブクラス化を許すと、型やインスタンスに関する重要な不変条件が破られます。 一方、複数の列挙型で共通の動作を共有できるようにすることには意味があります。 例は OrderedEnum を参照してください。
データクラスのサポート¶
dataclass を継承する場合、 __repr__() は継承するクラス名を省略します。 例えば:
>>> from dataclasses import dataclass, field
>>> @dataclass
... class CreatureDataMixin:
... size: str
... legs: int
... tail: bool = field(repr=False, default=True)
...
>>> class Creature(CreatureDataMixin, Enum):
... BEETLE = 'small', 6
... DOG = 'medium', 4
...
>>> Creature.DOG
<Creature.DOG: size='medium', legs=4>
標準の repr() を使うには、dataclass() の引数 repr=False を使用してください。
バージョン 3.12 で変更: dataclass のフィールドだけが値の領域に表示され、dataclassの名前は表示されなくなりました
注釈
Enum やそのサブクラスへの @~dataclasses.dataclass デコレータの適用はサポートされていません。
エラーは発生しませんが、異なるメンバーが等しいと判定されるなど、実行時に非常に奇妙な結果になります。
>>> @dataclass # 意味のない操作なので、このようにしてはいけない
... class Color(Enum):
... RED = 1
... BLUE = 2
...
>>> Color.RED is Color.BLUE
False
>>> Color.RED == Color.BLUE # ここが問題: 等しくなるべきではない
True
Pickle 化¶
列挙型は pickle 化と unpickle 化が行えます:
>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True
通常の pickle 化の制約が適用されます。 unpickle 化の際にモジュールから列挙型をインポートできる必要があるため、pickle 化する列挙型はモジュールのトップレベルで定義しなければなりません。
注釈
pickle プロトコルバージョン 4 では他のクラスで入れ子になった列挙型の pickle 化も容易です。
列挙型クラスに __reduce_ex__() を定義すると、メンバーの pickle 化・unpickle 化の方法を変更できます。
デフォルトでは値を使いますが、値が複雑な列挙型では名前を使う方法が適している場合があります。
>>> import enum
>>> class MyEnum(enum.Enum):
... __reduce_ex__ = enum.pickle_by_enum_name
注釈
フラグに名前による pickle 化を使うことは、名前の無いエイリアスが unpickle 化されないため推奨されません。
関数 API¶
Enum クラスは呼び出し可能で、以下の関数 API を提供しています:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
この API の動作は namedtuple と似ています。Enum 呼び出しの第 1 引数は列挙型の名前です。
第 2 引数は列挙型メンバーの名前の もと になるものです。
空白区切りの名前の文字列、名前のシーケンス、キーと値のペアを表す 2 要素タプルのシーケンス、名前を値に対応付ける辞書などのマッピングを指定できます。
後の 2 つでは、列挙型に任意の値を割り当てられます。
それ以外では 1 から始まる整数が順に自動で割り当てられます (開始値は start 引数で変更できます)。
Enum を継承する新しいクラスが返されます。
つまり、上記の Animal への代入は次と同じです。
>>> class Animal(Enum):
... ANT = 1
... BEE = 2
... CAT = 3
... DOG = 4
...
0 ではなく 1 をデフォルトの開始番号とする理由は、0 が真偽値としては False であり、デフォルトの列挙メンバーはすべて True 評価されるようにするためである。
関数 API で作った列挙型の pickle 化は、注意が必要な場合があります。 列挙型が作られたモジュールを特定するために、フレームスタックの実装の詳細を使うためです。 例えば、別モジュールのユーティリティ関数で作ると失敗し、IronPython や Jython でも動かない可能性があります。 解決策は、次のようにモジュール名を明示的に指定することです。
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
警告
module が指定されず、Enum 自身もモジュールを特定できない場合、新しい Enum メンバーは unpickle 化できません。
問題の発生源に近いところでエラーを出すため、pickle 化も無効になります。
新しい pickle プロトコル 4 も、場合によっては、pickle がクラスを見つけられる場所を __qualname__ に設定しておく必要があります。
例えば、グローバルスコープのクラス SomeData の中から、このクラスにアクセスできる場合は次のようにします。
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')
完全な構文は以下のようになります:
Enum(
value='NewEnumName',
names=<...>,
*,
module='...',
qualname='...',
type=<mixed-in class>,
start=1,
)
value: 新しい列挙型クラスの名前として記録する値。
names: enumのメンバー。 空白またはカンマで区切られた文字列 (値は特に指定がない限り1から始まります):
'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
または名前のイテレータで指定もできます:
['RED', 'GREEN', 'BLUE']
または (名前, 値) のペアのイテレータでも指定できます:
[('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]
またはマッピングでも指定できます:
{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
module: 新しい enum クラスが属するモジュールの名前です。
qualname: モジュール内で新しい列挙型クラスを見つけられる場所。
type: 新しい列挙型クラスにミックスインする型。
start:名前だけ渡された場合にカウントを開始する番号。
バージョン 3.5 で変更: start 引数が追加されました。
派生列挙型¶
IntEnum¶
提供されている 1 つ目の Enum の派生型であり、 int のサブクラスでもあります。
IntEnum のメンバーは整数と比較できます;
さらに言うと、異なる整数列挙型どうしでも比較できます:
>>> 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
ただし、これらも標準の Enum 列挙型とは比較できません:
>>> class Shape(IntEnum):
... CIRCLE = 1
... SQUARE = 2
...
>>> class Color(Enum):
... RED = 1
... GREEN = 2
...
>>> Shape.CIRCLE == Color.RED
False
IntEnum の値は他の用途では整数のように振る舞います:
>>> int(Shape.CIRCLE)
1
>>> ['a', 'b', 'c'][Shape.CIRCLE]
'b'
>>> [i for i in range(Shape.SQUARE)]
[0, 1]
StrEnum¶
提供されている2つ目の Enum の派生型もまた、 str のサブクラスでもあります。StrEnum のメンバーは、文字列と比較できます; さらに言うと、異なる文字列列挙型どうしでも比較できます。
Added in version 3.11.
IntFlag¶
次の Enum の派生型 IntFlag も、int を基底としています。
違いは、IntFlag のメンバーをビット演算子 (&, |, ^, ~) で組み合わせることができ、可能であれば結果も IntFlag のメンバーになる点です。
IntEnum と同様に、IntFlag のメンバーも整数なので、int を使える場所ならどこでも使えます。
注釈
IntFlag メンバーに対してビット演算以外のどんな演算をしても、その結果は IntFlag メンバーではなくなります。
ビット単位演算の結果が IntFlag として不正な値な値の場合、値は IntFlag メンバーではなくなります。 詳しくは FlagBoundary を参照してください。
Added in version 3.6.
バージョン 3.11 で変更.
IntFlag クラスの例:
>>> 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
組み合わせにも名前を付けられます:
>>> class Perm(IntFlag):
... R = 4
... W = 2
... X = 1
... RWX = 7
...
>>> Perm.RWX
<Perm.RWX: 7>
>>> ~Perm.RWX
<Perm: 0>
>>> Perm(7)
<Perm.RWX: 7>
注釈
組み合わせに名前をつけたものはエイリアスとみなされます。エイリアスはイテレーション中には表示されませんが、値による検索では返却されます。
バージョン 3.11 で変更.
IntFlag と Enum のもう 1 つの重要な違いは、フラグが設定されていない (値が0である) 場合、その真偽値としての評価は False になることです:
>>> Perm.R & Perm.X
<Perm: 0>
>>> bool(Perm.R & Perm.X)
False
IntFlag メンバーも int のサブクラスであるため、それらと組み合わせることができます(ただし、 IntFlag 型ではなくなる可能性があります):
>>> Perm.X | 4
<Perm.R|X: 5>
>>> Perm.X + 8
9
IntFlag メンバーは反復処理することもできます:
>>> list(RW)
[<Perm.R: 4>, <Perm.W: 2>]
Added in version 3.11.
Flag¶
最後の派生型は Flag です。
IntFlag と同様に、 Flag メンバーもビット演算子 (&, |, ^, ~) を使って組み合わせられます。
しかし IntFlag とは違い、他のどの Flag 列挙型とも int とも組み合わせたり、比較したりできません。
値を直接指定することも可能ですが、値として auto を使い、 Flag に適切な値を選ばせることが推奨されています。
Added in version 3.6.
IntFlag と同様に、 Flag メンバーの組み合わせがどのフラグも設定されていない状態になった場合、その真偽値としての評価は False となります:
>>> from enum import Flag, auto
>>> class Color(Flag):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color: 0>
>>> bool(Color.RED & Color.GREEN)
False
個別のフラグは 2 のべき乗 (1, 2, 4, 8, ...) の値を持つべきですが、フラグの組み合わせはそうはなりません:
>>> class Color(Flag):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
... WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
<Color.WHITE: 7>
"フラグが設定されていない" 状態に名前を付けても、その真偽値は変わりません:
>>> class Color(Flag):
... BLACK = 0
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.BLACK
<Color.BLACK: 0>
>>> bool(Color.BLACK)
False
Flag メンバーは反復処理することもできます:
>>> purple = Color.RED | Color.BLUE
>>> list(purple)
[<Color.RED: 1>, <Color.BLUE: 2>]
Added in version 3.11.
その他¶
IntEnum は enum モジュールの一部ですが、単独での実装もとても簡単に行なえます:
class IntEnum(int, ReprEnum): # ReprEnum の代わりに Enum でもよい
pass
このようにして、同様の派生列挙型を定義できます。
例えば、int の代わりに float をミックスインする FloatEnum も作れます。
Some rules:
Enumのサブクラスを作成するとき、複合させるデータ型は、基底クラスの並びでEnumクラス自身より先に記述しなければなりません (上記IntEnumの例を参照)。複合させる型はサブクラス化可能でなければいけません。例えば、
boolとrangeはサブクラス化できないため、複合させるとEnum作成時にエラーが発生します。Enumのメンバーはどんなデータ型でも構いませんが、追加のデータ型 (例えば、上の例のint) と複合させてしまうと、すべてのメンバーの値はそのデータ型でなければならなくなります。この制限は、メソッドの追加するだけの、他のデータ型を指定しない複合には適用されません。別のデータ型をミックスインした場合、
value属性は列挙型メンバー自身とは 同一ではありません。 ただし、同等の値であり、等価比較では等しいと判定されます。%-style formatting:
%sand%rcall theEnumclass's__str__()and__repr__()respectively; other codes (such as%ior%hfor IntEnum) treat the enum member as its mixed-in type.フォーマット済み文字列リテラル、
str.format()、format()は、列挙型の__str__()メソッドを使います。
__new__() と __init__() の使い分け¶
Enum メンバーの実際の値をカスタマイズする場合は、__new__() を使わなければなりません。
それ以外の変更は __new__() と __init__() のどちらでも行えますが、__init__() を使うほうが望ましいです。
例えば、複数の値をコンストラクタに渡すが、その中の1つだけを値として使いたい場合は次のようにします:
>>> class Coordinate(bytes, Enum):
... """
... 整数のコードで検索できる、バイナリコード付きの座標。
... """
... def __new__(cls, value, label, unit):
... obj = bytes.__new__(cls, [value])
... obj._value_ = value
... obj.label = label
... obj.unit = unit
... return obj
... PX = (0, 'P.X', 'km')
... PY = (1, 'P.Y', 'km')
... VX = (2, 'V.X', 'km/s')
... VY = (3, 'V.Y', 'km/s')
...
>>> print(Coordinate['PY'])
Coordinate.PY
>>> print(Coordinate(3))
Coordinate.VY
警告
super().__new__() は 呼び出さないでください。
検索専用の __new__ が見つかるためです。
代わりに、データ型を直接使ってください。
細かい点¶
サポートされる __dunder__ 名と _sunder_ 名¶
サポートされる __dunder__ 名と _sunder_ 名は、Enum の API ドキュメント に記載されています。
_Private__names¶
Private names は列挙型メンバーには変換されず、通常の属性となります。
バージョン 3.11 で変更.
Enum メンバー型¶
列挙型のメンバーは、その列挙型クラスのインスタンスであり、通常は EnumClass.member としてアクセスします。
列挙型の独自の動作を書く場合などには、あるメンバーから別のメンバーへ直接アクセスできると便利であり、この使い方もサポートされています。
ただし、メンバー名とミックスインしたクラスの属性やメソッド名との衝突を避けるため、メンバー名には大文字を使うことを強く推奨します。
バージョン 3.5 で変更.
ほかのデータ型と組み合わせたメンバーを作る¶
When subclassing other data types, such as int or str, with
an Enum, all values after the = are passed to that data type's
constructor. For example:
>>> class MyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer
... example = '11', 16 # つまり x='11'、base=16
...
>>> MyEnum.example.value # 16 進数の 11 は...
17
Enum クラスとメンバーの真偽値¶
(int, str などのような) 非 Enum 型と複合させた enum クラスは、その複合された型の規則に従って評価されます;
そうでない場合は、全てのメンバーは True と評価されます。
メンバーの値に依存する独自の enum の真偽値評価を行うには、クラスに次のコードを追加してください:
def __bool__(self):
return bool(self.value)
メソッド付きの Enum クラス¶
enum サブクラスに追加のメソッドを与えた場合、後述の Planet クラスのように、そのメソッドはメンバーの dir() に表示されますが、クラスの dir() には表示されません:
>>> dir(Planet)
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']
Flag のメンバーの組み合わせ¶
Flag メンバーの組み合わせをイテレートすると、単一ビットで構成されるメンバーだけが返されます。
>>> class Color(Flag):
... RED = auto()
... GREEN = auto()
... BLUE = auto()
... MAGENTA = RED | BLUE
... YELLOW = RED | GREEN
... CYAN = GREEN | BLUE
...
>>> Color(3) # 名前付きの組み合わせ
<Color.YELLOW: 3>
>>> Color(7) # 名前のない組み合わせ
<Color.RED|GREEN|BLUE: 7>
Flag と IntFlag の細部¶
例として以下のスニペットを使用します:
>>> class Color(IntFlag):
... BLACK = 0
... RED = 1
... GREEN = 2
... BLUE = 4
... PURPLE = RED | BLUE
... WHITE = RED | GREEN | BLUE
...
次のことが成り立ちます。
単一ビットのフラグは正規形です
複数ビットや0ビットのフラグはエイリアスです
反復処理では正規形のフラグのみ返却されます:
>>> list(Color.WHITE) [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
negating a flag or flag set returns a new flag/flag set with the corresponding positive integer value:
>>> Color.BLUE <Color.BLUE: 4> >>> ~Color.BLUE <Color.RED|GREEN: 3>
名前のないフラグについては、そのメンバーの名前から名前が生成されます:
>>> (Color.RED | Color.GREEN).name 'RED|GREEN' >>> class Perm(IntFlag): ... R = 4 ... W = 2 ... X = 1 ... >>> (Perm.R & Perm.W).name is None # 実質的には Perm(0) True
演算の結果として、複数ビットのフラグ、つまり別名が返されることもあります。
>>> Color.RED | Color.BLUE <Color.PURPLE: 5> >>> Color(7) # または Color(-1) <Color.WHITE: 7> >>> Color(0) <Color.BLACK: 0>
メンバーシップ/包含 のチェックでは、値が0のフラグは常に含まれるものとして扱われます:
>>> Color.BLACK in Color.WHITE True
それ以外では、一方のフラグの全ビットが他方のフラグに含まれる場合のみ、Trueが返されます:
>>> Color.PURPLE in Color.WHITE True >>> Color.GREEN in Color.PURPLE False
範囲外のビットや無効なビットをどう扱うかを制御する、新しい境界の仕組みがあります。
STRICT、CONFORM、EJECT、KEEP の 4 つです。
STRICT --> 無効な値が指定された場合に例外を発生させる
CONFORM --> 無効なビットを破棄する
EJECT --> フラグのステータスを失い、指定された値を持つ通常の int となります。
KEEP --> 余分なビットを保持する
Flag としての型と余分なビットを保持します。
余分なビットはイテレーションには現れません。
余分なビットは repr() と str() の結果には現れます。
The default for Flag is STRICT, the default for IntFlag is EJECT,
and the default for _convert_ is KEEP (see ssl.Options for an
example of when KEEP is needed).
EnumとFlagはどう違うのか?¶
Enum は Enum 派生クラスやそれらのインスタンス (メンバー) 双方の多くの側面に影響を及ぼすカスタムメタクラスを持っています。
Enum クラス¶
メタクラス EnumType は、__contains__()、__dir__()、__iter__() などのメソッドを提供します。
これにより、通常のクラスでは失敗する list(Color) や some_enum_var in Color のような操作を、Enum クラスで行えるようになります。
EnumType はまた、作成される Enum クラスの __new__()、__getnewargs__()、__str__()、__repr__() などの各メソッドが適切なものになるようにします。
Flag クラス¶
フラグでは、別名の意味が拡張されています。
正規のフラグであるには、値が 2 の累乗であり、ほかのメンバーの別名でないことが必要です。
そのため、Enum で別名となるものに加えて、値を持たないフラグ (つまり 0) や、複数の 2 の累乗の値を組み合わせたフラグ (例えば 3) も別名とみなされます。
Enum メンバー (インスタンス)¶
列挙型メンバーの最も興味深い点は、シングルトンであることです。
EnumType は列挙型クラスそのものを作る際に、すべてのメンバーを作ります。
その後、既存のメンバーインスタンスだけを返す独自の __new__() を設定し、新しいインスタンスが作られないようにします。
Flag メンバー¶
フラグのメンバーは、Flag クラスと同様に反復処理することができ、正規のメンバーのみが返されます。 例えば:
>>> list(Color)
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
(BLACK 、 PURPLE 、 WHITE は表示されないことに注意。)
フラグのメンバーを反転させると、負の値ではなく、対応する正の値が返されます:
>>> ~Color.RED
<Color.GREEN|BLUE: 6>
フラグのメンバーは、それが含む2のべき乗の値の数に対応するlengthを持ちます。 例えば:
>>> len(Color.PURPLE)
2
Enum クックブック¶
Enum, IntEnum, StrEnum, Flag, IntFlag は用途の大部分をカバーすると予想されますが、そのすべてをカバーできているわけではありません。ここでは、そのまま、あるいは独自の列挙型を作る例として使える、様々なタイプの列挙型を紹介します。
値の省略¶
多くの用途では、列挙型の実際の値が何かは気にされません。 このタイプの単純な列挙型を定義する方法はいくつかあります:
これらのどの方法を使ってもユーザーに対して、値は重要ではなく、他のメンバーの番号の振り直しをする必要無しに、メンバーの追加、削除、並べ替えが行えるということを示せます。
auto を使う¶
auto を使うと次のようになります:
>>> class Color(Enum):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN: 3>
object を使う¶
object を使うと次のようになります:
>>> class Color(Enum):
... RED = object()
... GREEN = object()
... BLUE = object()
...
>>> Color.GREEN
<Color.GREEN: <object object at 0x...>>
この例は、独自の __repr__() を書きたくなる理由もよく示しています。
>>> class Color(Enum):
... RED = object()
... GREEN = object()
... BLUE = object()
... def __repr__(self):
... return "<%s.%s>" % (self.__class__.__name__, self._name_)
...
>>> Color.GREEN
<Color.GREEN>
解説文字列を使う¶
値として文字列を使うと次のようになります:
>>> class Color(Enum):
... RED = 'stop'
... GREEN = 'go'
... BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN: 'go'>
独自の __new__() を使う¶
自動採番する __new__() を使うと、次のようになります。
>>> class AutoNumber(Enum):
... 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: 2>
AutoNumber をより広い用途で使うには、シグニチャに *args を追加します:
>>> class AutoNumber(Enum):
... def __new__(cls, *args): # 上の例からの変更はここだけ
... value = len(cls.__members__) + 1
... obj = object.__new__(cls)
... obj._value_ = value
... return obj
...
AutoNumber を継承すると、追加の引数を取り扱える独自の __init__ が書けます。
>>> class Swatch(AutoNumber):
... def __init__(self, pantone='unknown'):
... self.pantone = pantone
... AUBURN = '3497'
... SEA_GREEN = '1246'
... BLEACHED_CORAL = () # 新色なので、まだ Pantone コードはない!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'
注釈
__new__() を定義した場合、そのメソッドは Enum メンバーの作成時に使われます。
その後は Enum の __new__() に置き換えられ、クラス作成後の既存メンバーの検索に使われます。
警告
super().__new__() は 呼び出さないでください。
検索専用の __new__ が見つかるためです。
代わりに、例えば次のようにデータ型を直接使ってください。
obj = int.__new__(cls, value)
OrderedEnum¶
IntEnum をベースとしないため、通常の Enum の不変条件 (他の列挙型と比較できないなど) のままで、メンバーを順序付けできる列挙型です:
>>> 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¶
値が重複するメンバーがある場合に、エイリアスを作成するのではなくエラーを発生させます:
>>> 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'
注釈
これは Enum に別名を無効にするのと同様な振る舞いの追加や変更をおこなうためのサブクラス化に役立つ例です。単に別名を無効にしたいだけなら、 unique() デコレーターを使用して行えます。
MultiValueEnum¶
1 つのメンバーに複数の値を持たせられます。
>>> class MultiValueEnum(Enum):
... def __new__(cls, value, *values):
... self = object.__new__(cls)
... self._value_ = value
... for v in values:
... self._add_value_alias_(v)
... return self
...
>>> class DType(MultiValueEnum):
... float32 = 'f', 8
... double64 = 'd', 9
...
>>> DType('f')
<DType.float32: 'f'>
>>> DType(9)
<DType.double64: 'd'>
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 # 単位はキログラム
... self.radius = radius # 単位はメートル
... @property
... def surface_gravity(self):
... # 万有引力定数 (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¶
_ignore_ 属性の使用例です。
>>> import datetime as dt
>>> class Period(dt.timedelta, Enum):
... "さまざまな長さの時間"
... _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)>]
EnumType のサブクラスを作る¶
列挙型に関する要件の多くは、クラスデコレータや独自の関数で Enum のサブクラスをカスタマイズすれば満たせます。
一方、EnumType のサブクラスを作れば、Enum 自体の使い方を変えることもできます。