web-dev-qa-db-ja.com

Python、Enum型は何に適していますか?

Python 3.4では、標準ライブラリenumに列挙ライブラリがありました。 Python 2.4〜2.7(さらには3.1〜3.3)で動作するenumのバックポートを取得できます。pypiでは enum34 です。

しかし、私たちはこの新しいモジュールなしでかなり長い間うまくいくことができました。

私は、他の言語の列挙型の目的について一般的な考えを持っています。 Pythonでは、次のようにベアクラスを使用し、これを列挙型として参照することが一般的です。

class Colors:
    blue = 1
    green = 2
    red = 3

これをAPIで使用して、値の正規表現を作成できます。例:

function_of_color(Colors.green)

これに批判がある場合、それは変更可能であり、反復することはできません(簡単に)、そしてどのように整数のセマンティクス2を知るのですか?

次に、名前付きタプルのようなものを使用することができると思いますが、これは不変です?

>>> Colors = namedtuple('Colors', 'blue green red')
>>> colors = Colors('blue', 'green', 'red')
>>> colors
Colors(blue='blue', green='green', red='red')
>>> list(colors)
['blue', 'green', 'red']
>>> len(colors)
3
>>> colors.blue
'blue'
>>> colors.index(colors.blue)
0

Namedtupleの作成は少し冗長です(各名前を2回記述する必要があります)。色の「数」を取得することも少し洗練されていません(colorsを2回記述する必要があります)。文字列を使用して値のチェックを行う必要がありますが、これは少し効率が低下します。

列挙に戻ります。

列挙型の目的は何ですか?彼らは言語に対してどのような価値を生み出しますか?それらをいつ使用し、いつ使用を避けるべきですか?

29
Aaron Hall

列挙型の目的は何ですか?彼らは言語に対してどのような価値を生み出しますか?それらをいつ使用し、いつ使用を避けるべきですか?

EnumタイプがPython via PEP 435 になりました。指定された理由は次のとおりです。

列挙のプロパティは、セマンティックな意味を持つ場合と持たない場合がある不変の関連する定数値のセットを定義するのに役立ちます。

この目的で数字と文字列を使用する場合、それらは "magic numbers" または "magic strings"として特徴付けられます。数字にセマンティクスが含まれることはめったになく、文字列は簡単に混同されます(大文字、スペル、スネーク、キャメルケースなど)。

曜日や学校の成績は、この種の価値のコレクションの例です。

docs の例を次に示します。

_from enum import Enum

class Color(Enum):
    red = 1
    green = 2
    blue = 3
_

ベアクラスのように、これはnamedtupleの例よりもはるかに読みやすくエレガントです。また、不変であり、以下に示すようにさらに利点があります。

厳密に支配的:列挙型メンバーの型は列挙型です

_>>> type(Color.red)
<enum 'Color'>
>>> isinstance(Color.green, Color)
True
_

これにより、Enum定義でメンバーの機能を定義できます。値に関する機能の定義は、他の従来の方法で実現できますが、非常に洗練されていません。

改善:文字列強制

文字列表現は人間が読める形式ですが、reprにはさらに情報があります。

_>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>
_

これは、マジックナンバーの改善であり、名前付きタプルの文字列よりも優れている可能性があります。

反復(パリティ):

列挙型は、namedtupleのような反復をサポートしますが、むき出しのクラスではありません):

_>>> for color in Color:
        print(color)
Color.red
Color.green
Color.blue
_

___members___属性は、enumの名前をそれぞれのenumオブジェクトにマッピングするOrderedDictです(namedtupleの_asdict()関数に似ています)。

Pickleでサポート(パリティ)

列挙型をシリアル化および逆シリアル化できます(誰かがこれを心配している場合)。

_>>> import pickle
>>> color.red is pickle.loads(pickle.dumps(color.red))
True
_

改善:エイリアス

これは、ベアクラスにはない素晴らしい機能であり、namedtupleにエイリアスがあることを伝えるのは難しいでしょう。

_class Color(Enum):
    red = 1
    green = 2
    blue = 3
    really_blue = 3
_

エイリアスは正規名の後にありますが、どちらも同じです:

_>>> Color.blue is Color.really_blue
True
_

値の衝突を避けるためにエイリアスを禁止する必要がある場合は、_enum.unique_デコレーター(厳密に支配的な機能)を使用します。

厳密に支配的:isで行われた比較

列挙型はisを使用してテストすることを目的としています。これは、プロセス内の単一オブジェクトのIDをすばやくチェックするものです。

_>>> Color.red is Color.red
True
>>> Color.red is Color.blue
False
>>> Color.red is not Color.blue
True
_

同等性のテストも機能しますが、isとの同一性のテストが最適です。

他のPythonクラスとは異なるセマンティクス

Enumクラスは、通常のPythonタイプとは異なるセマンティクスを持ちます。Enumの値はEnumのインスタンスであり、それらの値のメモリ内のシングルトンです-インスタンス化する他の目的はありません。

_>>> Color.red is Color('red')
_

これは心に留めておくことが重要です。おそらくマイナス面ですが、この次元で比較することはリンゴとオレンジを比較することです。

順序付けられていないと見なされる列挙型

Enumクラスはメンバーが作成される順序を知っていますが、enumは順序付けられているとは見なされません。列挙される可能性のあるものの多くは自然な順序を持たないため、これは機能です。したがって、順序は任意です。

ただし、列挙型の順序を指定できます(次のセクションを参照)。

サブクラス化

宣言されたメンバーでEnumをサブクラス化することはできませんが、動作を共有するメンバーを宣言しないEnumをサブクラス化できますcanのOrderedEnumレシピを参照してください docs )。

これは機能です-メンバーでEnumをサブクラス化することはほとんど意味がありませんが、ここでも比較はリンゴとオレンジです。

いつ_enum.Enum_を使用すべきですか?

これは、Pythonの新しい標準列挙です。共同編集者は、列挙型がこれらの列挙型のように動作することを期待します。

任意のデータの代わりに、正規名を使用するように明示的に指定するコード内の列挙データの正規ソースがある場所であればどこでも使用します。

たとえば、コード内で、ユーザーが_"Green"_、_"green"_、2、または_"Greene"_ではなく、_Color.green_であると宣言する場合、enum.Enumオブジェクトを使用します。明示的かつ具体的です。

ドキュメント には多くの例とレシピがあります。

いつ避けるべきですか?

自分で転がしたり、人々に魔法の数字や文字列を推測させたりするのを止めてください。それらを避けないでください。それらを受け入れます。

ただし、歴史的な理由で列挙型メンバーが整数である必要がある場合、同じモジュールからのIntEnumがあり、同じ動作をしますが、組み込みのintをサブクラス化するため整数でもありますEnumをサブクラス化する前。 IntEnumのヘルプから:

_class IntEnum(builtins.int, Enum)
_

intEnum値がintのインスタンスとしてテストされることがわかります。

59
Aaron Hall