1. ホーム
  2. python

[解決済み] Python、Enum型は何のためにあるのか?重複

2022-11-03 21:03:05

質問

Python 3.4では、標準ライブラリにEnumライブラリが追加されました。 enum . をバックポートすることができます。 enum は Python 2.4 から 2.7 (そして 3.1 から 3.3 でも) で動作するバックポートが得られます。 enum34 をpypiに追加しました。

しかし、私たちはこの新しいモジュールなしでかなりの期間うまくやってこれました - ではなぜ今、私たちはこのモジュールを手に入れたのでしょうか?

私は他の言語からenumの目的について一般的な考えを持っています。Pythonでは、以下のように素のクラスを使って、これをenumと呼ぶのが一般的でした。

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

これは、APIで値の正規表現を作成する場合などに使用することができます。

function_of_color(Colors.green)

これに批判があるとすれば、ミュータブルであること、(簡単に)反復処理できないこと、そして、整数のセマンティクスをどうやって知るのか、ということです。 2 ?

それならnamedtupleのようなものを使えばいいと思うのですが、これは不変なのでしょうか?

>>> 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回書かなければならない)、ややエレガントではありません。色の "number" を取得するのも、少し優雅ではありません。 colors と2回書かなければならない)。値のチェックは文字列で行う必要があり、少し効率が悪くなる。

というわけで、列挙に戻ります。

列挙型は何のためにあるのでしょうか?言語にとってどんな価値があるのでしょうか?いつ使って、いつ避けるべきなのか?

どのように解決するのですか?

<ブロッククオート

列挙型は何のためにあるのですか?列挙型は言語にとってどのような価値を生むのでしょうか?いつ使って、いつ避けるべきなのか?

Enum型がPythonに導入されたのは PEP 435 . 与えられた理由は

列挙型のプロパティは、不変で関連性のある定数値のセットを定義するのに便利で、意味的な意味を持つことも持たないこともあります。

この目的のために数値や文字列を使用する場合、次のように特徴付けることができます。 "マジックナンバー"。 または "魔法の文字列" と呼ばれます。数字はほとんど意味を持ちませんし、文字列は簡単に混同されます (大文字小文字、スペル、スネークケースかキャメルケースか)。

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

の例です。 docs :

from enum import Enum

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

素のクラスと同様に、これはnamedtupleの例よりもずっと読みやすくエレガントで、また不変であり、以下で見るようにさらなる利点があります。

厳密には支配的です。enum メンバの型は enum

>>> 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からの文字列よりも優れている可能性さえありますね。

イテレーション(パリティ)。

enumも反復処理をサポートしています(namedtupleのような、素のクラスはそうでもないような)。

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

__members__ 属性は、列挙型の名前とそれぞれの列挙型オブジェクトの順序付きマッピングです(namedtuple の _asdict() 関数に似ています)。

>>> Color.__members__
mappingproxy(OrderedDict([('red', <Color.red: 1>), ('green', <Color.green: 2>), 
('blue', <Color.blue: 3>)]))

pickleで対応(パリティ)

enumのシリアライズとデシリアライズができます(心配している人がいたら念のため)。

>>> 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 で、これはプロセス中の単一のオブジェクトの同一性を高速にチェックするものです。

>>> 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(1)

これは重要なことで、もしかしたらデメリットかもしれませんが、この次元で比較することは、リンゴとオレンジを比較することです。

順序付きであることを前提としない列挙型

Enum クラスはメンバーがどのような順序で作成されるかを知っていますが、列挙型は順序を仮定していません。これは、列挙される可能性のある多くのものが自然な順序を持たないため、順序が恣意的になってしまうという特徴です。

しかし、列挙に順序を与えることはできます(次のセクションを参照してください)。

サブクラス化

メンバーが宣言されているEnumをサブクラス化することはできませんが はできます。 のOrderedEnumレシピを参照してください)。 ドキュメント ).

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

いつ enum.Enum ?

これは Python の新しい標準的な列挙です。Collaboratorはあなたの列挙がこれらの列挙のように動作することを期待します。

コード内の列挙データの正規のソースがあり、任意のデータではなく、正規の名前を使用するように明示的に指定したい場合はどこでも使用します。

例えば、あなたのコードで、ユーザーにそれが "Green" , "green" 2、または "Greene" が、しかし Color.green - はenum.Enumオブジェクトを使用します。これは明示的かつ具体的です。

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

どのような場合に避けるべきですか?

自分で転がしたり、魔法の数字や文字列を推測させるのはやめましょう。避けるべきではありません。受け入れるのです。

しかし、歴史的な理由で enum のメンバが整数であることが要求される場合、そのような場合は IntEnum があります。これは同じ振る舞いをしますが、組み込みの int をサブクラス化する前に Enum . から IntEnum のヘルプを参照してください。

class IntEnum(builtins.int, Enum)

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