enum
--- 列挙型のサポート¶
バージョン 3.4 で追加.
ソースコード: Lib/enum.py
列挙型は、一意の定数値に束縛された識別名 (メンバー) の集合です。列挙型の中でメンバーの同一性を比較でき、列挙型自身でイテレートが可能です。
注釈
Enumメンバーは大文字/小文字?
Enumクラス群は定数を表現するために使われるため、列挙型のメンバーの名前にはUPPER_CASEを使うことを推奨します。本ページのドキュメントのサンプルでもそのスタイルを採用します。
モジュールコンテンツ¶
このモジュールでは一意の名前と値の集合を定義するのに使用できる 4 つの列挙型クラス Enum
, IntEnum
, Flag
, IntFlag
を定義しています。
このモジュールはデコレータの unique()
とヘルパークラスの auto
も定義しています。
-
class
enum.
IntFlag
¶ 列挙型定数を作成する基底クラスで、ビット演算子を使って組み合わせられ、その結果も
IntFlag
メンバーになります。IntFlag
はint
のサブクラスでもあります。
-
enum.
unique
() 一つの名前だけがひとつの値に束縛されていることを保証する Enum クラスのデコレーターです。
-
class
enum.
auto
¶ インスタンスはそれぞれ、適切な値で置き換えられます。値は、1からはじまります。
バージョン 3.6 で追加: Flag
, IntFlag
, auto
Enum の作成¶
列挙型は読み書きが容易になるよう class
文を使って作成します。もうひとつの作成方法は 機能 API で説明しています。列挙型は以下のように Enum
のサブクラスとして定義します:
>>> from enum import Enum
>>> class Color(Enum):
... RED = 1
... GREEN = 2
... BLUE = 3
...
注釈
列挙型のメンバー値
メンバー値は何であっても構いません: int
, str
などなど。
正確な値が重要でない場合は、 auto
インスタンスを使っておくと、適切な値が選ばれます。
auto
とそれ以外の値を混ぜて使う場合は注意する必要があります。
注釈
用語
クラス
Color
は 列挙型 (または Enum) です属性
Color.RED
,Color.GREEN
などは 列挙型のメンバー (または Enum メンバー) で、機能的には定数です。列挙型のメンバーは 名前 と 値 を持ちます (
Color.RED
の名前はRED
、Color.BLUE
の値は3
など。)
注釈
Enum の作成に class
文を使用するものの、Enum は通常の Python クラスではありません。詳細は Enum はどう違うのか? を参照してください。
列挙型のメンバーは人が読める文字列表現を持ちます:
>>> print(Color.RED)
Color.RED
...その一方でそれらの repr
はより多くの情報を持っています:
>>> print(repr(Color.RED))
<Color.RED: 1>
列挙型メンバーの データ型 はそれが所属する列挙型になります:
>>> type(Color.RED)
<enum 'Color'>
>>> isinstance(Color.GREEN, Color)
True
>>>
Enum メンバーは自身の名前を持つだけのプロパティも持っています:
>>> print(Color.RED.name)
RED
列挙型は定義順でのイテレーションをサポートしています:
>>> class Shake(Enum):
... VANILLA = 7
... CHOCOLATE = 4
... COOKIES = 9
... MINT = 3
...
>>> for shake in Shake:
... print(shake)
...
Shake.VANILLA
Shake.CHOCOLATE
Shake.COOKIES
Shake.MINT
列挙型のメンバーはハッシュ化可能なため、辞書や集合で使用できます:
>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True
列挙型メンバーおよびそれらの属性へのプログラム的アクセス¶
プログラム的にメンバーに番号でアクセスしたほうが便利な場合があります (すなわち、プログラムを書いている時点で正確な色がまだわからなく、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: Attempted to reuse key: 'SQUARE'
ただし、複数の列挙型メンバーが同じ値を持つことはできます。同じ値を持つ 2 つのメンバー A および B (先に定義したのは A) が与えられたとき、B は A の別名になります。A および B を値で調べたとき、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>
注釈
すでに定義されている属性と同じ名前のメンバー (一方がメンバーでもう一方がメソッド、など) の作成、あるいはメンバーと同じ名前の属性の作成はできません。
番号付けの値が同一であることの確認¶
デフォルトでは、前述のように複数の名前への同じ値の定義は別名とすることで許されています。この挙動を望まない場合、以下のデコレーターを使用することで各値が列挙型内で一意かどうか確認できます:
-
@
enum.
unique
¶
列挙型専用の class
デコレーターです。列挙型の __members__
に別名がないかどうか検索します; 見つかった場合、ValueError
が詳細情報とともに送出されます:
>>> 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()
...
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
その値は _generate_next_value_()
によって選ばれ、この関数はオーバーライドできます:
>>> class AutoName(Enum):
... def _generate_next_value_(name, start, count, last_values):
... return name
...
>>> class Ordinal(AutoName):
... NORTH = auto()
... SOUTH = auto()
... EAST = auto()
... WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]
注釈
デフォルトの _generate_next_value_()
メソッドの目的は、最後に提供した int
の次から順々に int
を提供することですが、この動作は実装詳細であり変更される可能性があります。
注釈
_generate_next_value_()
メソッドは他のメンバーよりも前に定義される必要があります。
イテレーション¶
列挙型のメンバーのイテレートは別名をサポートしていません:
>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
特殊属性 __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']
比較¶
列挙型メンバーは同一性を比較できます:
>>> 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
非列挙型の値との比較は常に不等となります (繰り返しになりますが、IntEnum
はこれと異なる挙動になるよう設計されています):
>>> Color.BLUE == 2
False
列挙型で許されるメンバーと属性¶
上述の例では列挙型の値に整数を使用しています。整数の使用は短くて使いやすい (そして 機能 API でデフォルトで提供されています) のですが、厳密には強制ではありません。ほとんどの事例では列挙型の実際の値が何かを気にしていません。しかし、値が重要で ある 場合、列挙型は任意の値を持つことができます。
列挙型は Python のクラスであり、通常どおりメソッドや特殊メソッドを持つことができます:
>>> class Mood(Enum):
... FUNKY = 1
... HAPPY = 3
...
... def describe(self):
... # self is the member here
... return self.name, self.value
...
... def __str__(self):
... return 'my custom str! {0}'.format(self.value)
...
... @classmethod
... def favorite_mood(cls):
... # cls here is the enumeration
... return cls.HAPPY
...
上記の結果が以下のようになります:
>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'
何が許されているかのルールは次のとおりです。先頭と末尾が 1 個のアンダースコアの名前は列挙型により予約されているため、使用できません。列挙型内で定義されたその他すべての名前は、その列挙型のメンバーとして使用できます。特殊メソッド (__str__()
, __add__()
など) と、メソッドを含むデスクリプタ(記述子)、 _ignore_
に記載されている変数名は例外です。
注意: 列挙型で __new__()
および/または __init__()
を定義した場合、列挙型メンバーに与えられた値はすべてこれらのメソッドに渡されます。例 Planet を参照してください。
Enumのサブクラス化の制限¶
新しい 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: Cannot extend enumerations
以下のような場合は許されます:
>>> class Foo(Enum):
... def some_behavior(self):
... pass
...
>>> class Bar(Foo):
... HAPPY = 1
... SAD = 2
...
メンバーが定義された列挙型のサブクラス化を許可すると、いくつかのデータ型およびインスタンスの重要な不変条件の違反を引き起こします。とはいえ、それが許可されると、列挙型のグループ間での共通の挙動を共有するという利点もあります。 (OrderedEnum の例を参照してください。)
Pickle 化¶
列挙型は pickle 化と unpickle 化が行えます:
>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True
通常の pickle 化の制限事項が適用されます: pickle 可能な列挙型はモジュールのトップレベルで定義されていなくてはならず、unpickle 化はモジュールからインポート可能でなければなりません。
注釈
pickle プロトコルバージョン 4 では他のクラスで入れ子になった列挙型の pickle 化も容易です。
Enum メンバーをどう pickle 化/unpickle 化するかは、列挙型クラス内の __reduce_ex__()
で定義することで変更できます。
機能 API¶
Enum
クラスは呼び出し可能で、以下の機能 API を提供しています:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> Animal.ANT.value
1
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
この API の動作は namedtuple
と似ています。Enum
呼び出しの第 1 引数は列挙型の名前です。
第 2 引数は列挙型メンバー名の ソース です。空白で区切った名前の文字列、名前のシーケンス、キー/値のペアの 2 要素タプルのシーケンス、あるいは名前と値のマッピング (例: 辞書) を指定できます。最後の 2 個のオプションでは、列挙型へ任意の値を割り当てることができます。前の 2 つのオプションでは、1 から始まり増加していく整数を自動的に割り当てます (別の開始値を指定するには、start
引数を使用します)。Enum
から派生した新しいクラスが返されます。言い換えれば、上記の Animal
への割り当ては以下と等価です:
>>> class Animal(Enum):
... ANT = 1
... BEE = 2
... CAT = 3
... DOG = 4
...
デフォルトの開始番号が 0
ではなく 1
である理由は、0
がブール演算子では False
になりますが、すべての列挙型メンバーの評価は True
でなければならないためです。
機能 API による Enum の 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
新しい Enum クラスに記録されるそれ自身の名前です。
- 名前
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
新しい Enum クラスが属するモジュールの場所です。
- type
新しい Enum クラスに複合されるデータ型です。
- start
names のみが渡されたときにカウントを開始する数です。
バージョン 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]
IntFlag¶
提供されている 2 つ目の Enum
の派生型 IntFlag
も int
を基底クラスとしています。
IntFlag
メンバーが Enum
メンバーと異なるのは、ビット演算子 (&, |, ^, ~) を使って組み合わせられ、その結果も IntFlag
メンバーになることです。
しかし、名前が示すように、 IntFlag
は int
のサブクラスでもあり、 int
が使われるところでもどこでも使えます。
IntFlag
メンバーに対してビット演算以外のどんな演算をしても、その結果は IntFlag
メンバーではなくなります。
バージョン 3.6 で追加.
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.-8: -8>
IntFlag
と Enum
のもう 1 つの重要な違いは、フラグが設定されていない (値が0である) 場合、その真偽値としての評価は False
になることです:
>>> Perm.R & Perm.X
<Perm.0: 0>
>>> bool(Perm.R & Perm.X)
False
IntFlag
は int
のサブクラスでもあるので、その両者を組み合わせられます:
>>> Perm.X | 8
<Perm.8|X: 9>
Flag¶
最後の派生型は Flag
です。
IntFlag
と同様に、 Flag
メンバーもビット演算子 (&, |, ^, ~) を使って組み合わせられます。
しかし IntFlag
とは違い、他のどの Flag
列挙型とも int
とも組み合わせたり、比較したりできません。
値を直接指定することも可能ですが、値として auto
を使い、 Flag
に適切な値を選ばせることが推奨されています。
バージョン 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: 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
その他¶
IntEnum
は enum
モジュールの一部ですが、単独での実装もとても簡単に行なえます:
class IntEnum(int, Enum):
pass
ここでは似たような列挙型の派生を定義する方法を紹介します; 例えば、StrEnum
は int
ではなく str
で複合させたものです。
いくつかのルール:
Enum
のサブクラスを作成するとき、複合させるデータ型は、基底クラスの並びでEnum
自身より先に記述しなければなりません (上記IntEnum
の例を参照)。Enum
のメンバーはどんなデータ型でも構いませんが、追加のデータ型 (例えば、上の例のint
) と複合させてしまうと、すべてのメンバーの値はそのデータ型でなければならなくなります。 この制限は、メソッドの追加するだけの、int
やstr
のような他のデータ型を指定しない複合には適用されません。他のデータ型と複合された場合、
value
属性は、たとえ等価であり等価であると比較が行えても、列挙型メンバー自身としては 同じではありません 。%-方式の書式: %s および %r はそれぞれ
Enum
クラスの__str__()
および__repr__()
を呼び出します; その他のコード (IntEnum の %i や %h など) は列挙型のメンバーを複合されたデータ型として扱います。フォーマット済み文字列リテラル 、
str.format()
、format()
は、__str__()
やサブクラスでオーバーライドされた__format__()
よりもミックスイン型の__format__()
を優先して使います。!sや!rフォーマットコードを使うと、Enum
クラスの__str__()
や__repr__()
メソッドが強制的に使われます。
__init__()
と __init__()
のどちらを使うべきか¶
__new__()
は Enum
メンバーの実際の値をカスタマイズしたいときに利用します。他の変更を加える場合、 __new__()
と __init__()
のどちらを利用するかは、__init__()
の方が望ましいでしょう。
例えば、複数の値をコンストラクタに渡すが、その中の1つだけを値として使いたい場合は次のようにします:
>>> class Coordinate(bytes, Enum):
... """
... Coordinate with binary codes that can be indexed by the int code.
... """
... 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
興味深い例¶
Enum
, IntEnum
, IntFlag
, Flag
は用途の大部分をカバーすると予想されますが、そのすべてをカバーできているわけではありません。
ここでは、そのまま、あるいは独自の列挙型を作る例として使える、様々なタイプの列挙型を紹介します。
値の省略¶
多くの用途では、列挙型の実際の値が何かは気にされません。 このタイプの単純な列挙型を定義する方法はいくつかあります:
これらのどの方法を使ってもユーザーに対して、値は重要ではなく、他のメンバーの番号の振り直しをする必要無しに、メンバーの追加、削除、並べ替えが行えるということを示せます。
どの方法を選んでも、(重要でない) 値を隠す repr()
を提供すべきです:
>>> class NoValue(Enum):
... def __repr__(self):
... return '<%s.%s>' % (self.__class__.__name__, self.name)
...
auto
を使う¶
auto
を使うと次のようになります:
>>> class Color(NoValue):
... RED = auto()
... BLUE = auto()
... GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN>
object
を使う¶
object
を使うと次のようになります:
>>> class Color(NoValue):
... RED = object()
... GREEN = object()
... BLUE = object()
...
>>> Color.GREEN
<Color.GREEN>
解説文字列を使う¶
値として文字列を使うと次のようになります:
>>> class Color(NoValue):
... RED = 'stop'
... GREEN = 'go'
... BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
'go'
独自の __new__()
を使う¶
自動で番号を振る __new__()
を使うと次のようになります:
>>> class AutoNumber(NoValue):
... 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>
>>> Color.GREEN.value
2
AutoNumber
をより広い用途で使うには、シグニチャに *args
を追加します:
>>> class AutoNumber(NoValue):
... def __new__(cls, *args): # this is the only change from above
... 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 = () # New color, no Pantone code yet!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'
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()
デコレーターを使用して行えます。
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 # in kilograms
... self.radius = radius # in meters
... @property
... def surface_gravity(self):
... # universal gravitational constant (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_
属性の使用方法のサンプルです:
>>> from datetime import timedelta
>>> class Period(timedelta, Enum):
... "different lengths of time"
... _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)>]
Enum はどう違うのか?¶
Enum は Enum 派生クラスやそれらのインスタンス (メンバー) 双方の多くの側面に影響を及ぼすカスタムメタクラスを持っています。
Enum クラス¶
EnumMeta
メタクラスは、__contains__()
、__dir__()
、__iter__()
および標準的なクラスでは失敗するが Enum
クラスでは動作するその他のメソッド (list(Color) や some_enum_var in Color など) を責任を持って提供します。EnumMeta
は最終的な Enum
クラスのさまざまなメソッド (__new__()
、__getnewargs__()
、__str__()
および __repr__()
) が正しいことを責任を持って保証します。
Enum メンバー (インスタンス)¶
Enum メンバーについて最も興味深いのは、それらがシングルトンであるということです。EnumMeta
は Enum
自身を作成し、メンバーを作成し、新しいインスタンスが作成されていないかどうかを確認するために既存のメンバーインスタンスだけを返すカスタム __new__()
を追加します。
細かい点¶
__dunder__
名のサポート¶
__members__
は読み込み専用の、 member_name
:member
を要素とする順序付きマッピングです。これはクラスでのみ利用可能です。
__new__()
が、もし指定されていた場合、列挙型のメンバーを作成し、返します;
そのメンバー の _value_
を適切に設定するのも非常によい考えです。
いったん全てのメンバーが作成されると、それ以降 __new__()
は使われません。
_sunder_
名のサポート¶
_name_
-- メンバー名_value_
-- メンバーの値;__new__
で設定したり、変更したりできます_missing_
-- 値が見付からなかったときに使われる検索関数; オーバーライドされていることがあります_ignore_
-- a list of names, either as alist()
or astr()
, that will not be transformed into members, and will be removed from the final class_order_
-- Python 2/3のコードでメンバーの順序を固定化するのに利用されます(クラス属性で、クラス作成時に削除されます)_generate_next_value_
-- Functional API から利用され、auto
が列挙型のメンバーの適切な値を取得するのに使われます。オーバーライドされます。
バージョン 3.6 で追加: _missing_
, _order_
, _generate_next_value_
バージョン 3.7 で追加: _ignore_
Pythono 2 / Python 3のコードの同期を取りやすくするために _order_
属性を提供できます。実際の列挙値の順序と比較して一致してなければエラーを送出します:
>>> class Color(Enum):
... _order_ = 'RED GREEN BLUE'
... RED = 1
... BLUE = 3
... GREEN = 2
...
Traceback (most recent call last):
...
TypeError: member order does not match _order_
注釈
Python 2のコードでは _order_
属性は定義順が記録される前消えてしまうため、重要です。
Enum
メンバー型¶
Enum
メンバーは、それらの Enum
クラスのインスタンスで、通常は EnumClass.member
のようにアクセスします。
ある状況下では、 EnumClass.member.member
としてもアクセスできますが、この方法は絶対に使うべきではありません。
というのは、この検索は失敗するか、さらに悪い場合には、探している Enum
メンバー以外のものを返す場合もあるからです (これがメンバーの名前に大文字のみを使うのが良い理由の 1 つでもあります):
>>> class FieldTypes(Enum):
... name = 0
... value = 1
... size = 2
...
>>> FieldTypes.value.size
<FieldTypes.size: 2>
>>> FieldTypes.size.value
2
バージョン 3.5 で変更.
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__', 'name', 'surface_gravity', 'value']
Flag
のメンバーの組み合わせ¶
Flag メンバーの組み合わせに名前が無い場合、 repr()
の出力には、その値にある全ての名前を持つフラグと全ての名前を持つ組み合わせが含まれます:
>>> class Color(Flag):
... RED = auto()
... GREEN = auto()
... BLUE = auto()
... MAGENTA = RED | BLUE
... YELLOW = RED | GREEN
... CYAN = GREEN | BLUE
...
>>> Color(3) # named combination
<Color.YELLOW: 3>
>>> Color(7) # not named combination
<Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>