Pythonで「列挙型」を表すにはどうすればよいですか?


1143

私は主にC#開発者ですが、現在Pythonでプロジェクトに取り組んでいます。

PythonでEnumに相当するものをどのように表すことができますか?

回答:


2688

PEP 435で説明されているように、列挙型がPython 3.4に追加されました。また、pypiでは3.3、3.2、3.1、2.7、2.6、2.5、2.4にバックポートされています。

より高度なEnumテクニックについては、aenumライブラリを試してください(2.7、3.3以降、と同じ作者enum34。コードはpy2とpy3の間で完全に互換性がありません。たとえば__order__、Python 2で必要になります)。

  • を使用するenum34には、$ pip install enum34
  • を使用するaenumには、$ pip install aenum

インストールenum(何の数字は)完全に異なると互換性のないバージョンをインストールしません。


from enum import Enum     # for enum34, or the stdlib version
# from aenum import Enum  # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')

Animal.ant  # returns <Animal.ant: 1>
Animal['ant']  # returns <Animal.ant: 1> (string lookup)
Animal.ant.name  # returns 'ant' (inverse lookup)

または同等:

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

以前のバージョンでは、列挙型を実現する1つの方法は次のとおりです。

def enum(**enums):
    return type('Enum', (), enums)

これは次のように使用されます:

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'

次のようなものを使用して、自動列挙を簡単にサポートすることもできます。

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

次のように使用します:

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1

値を名前に変換するためのサポートは、次の方法で追加できます。

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)

これはその名前のすべてを上書きしますが、出力で列挙型をレンダリングするのに役立ちます。リバースマッピングが存在しない場合、KeyErrorがスローされます。最初の例では:

>>> Numbers.reverse_mapping['three']
'THREE'

1
私は理解できませんでした、なぜそれらはメソッドenum(* sequential、** named)でkwargs(** named)を渡したのですか?説明してください。クワーグなしでも動作します。確認しました。
Seenu S 2017

Python 2の関数を更新して、Python 3のEnum(name、values)の機能APIと互換性を持つようにするとよいでしょう
bscan

VaRのkwargsから(**named古いバージョンのための列挙機能で)カスタム値をサポートすることです:enum("blue", "red", "green", black=0)
エリックアラウージョ

823

PEP 435以前は、Pythonには同等の機能がありませんでしたが、独自に実装することができました。

私自身、私はそれをシンプルに保つのが好きです(ネット上で恐ろしく複雑な例を見てきました)。

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4(PEP 435)では、Enumを基本クラスにすることができます。これにより、PEPで説明されている追加機能が少し得られます。たとえば、列挙型メンバーは整数とは異なり、a nameとaで構成されvalueます。

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

print(Animal.DOG)
# <Animal.DOG: 1>

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

値を入力したくない場合は、次のショートカットを使用します。

class Animal(Enum):
    DOG, CAT = range(2)

Enum実装はリストに変換でき、反復可能です。メンバーの順序は宣言の順序であり、それらの値とは関係ありません。例えば:

class Animal(Enum):
    DOG = 1
    CAT = 2
    COW = 0

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True

51
いいえ、それはクラス変数です。
GeorgSchölly2009年

246
Pythonはデフォルトで動的です。Pythonのような言語でコンパイル時の安全性を強制する正当な理由はありません。特に、何もない場合です。そしてもう1つ、良いパターンは、それが作成されたコンテキストでのみ良いものです。使用しているツールによっては、優れたパターンが置き換えられるか、まったく役に立たなくなる場合もあります。
Alexandru Nedelcu

20
@Longpokeに100の値がある場合、間違いなく何か間違っていることになります;)私は列挙型に関連付けられた数値が好きです...それらは書きやすく(文字列に対して)、データベースに簡単に永続化でき、互換性がありますC / C ++列挙型。マーシャリングが容易になります。
Alexandru Nedelcu

50
私はこれを使用し、数字をに置き換えましたobject()
東武

9
元のPEP354は、単に拒否されるだけでなく、置き換えられるようになりました。PEP435は、Python 3.4の標準Enumを追加します。python.org/dev/peps/pep-0435を
Peter Hansen

322

ここに1つの実装があります:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

これがその使用法です:

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)

51
優秀な。これは、さらにオーバーライドすることによって改善することができる__setattr__(self, name, value)かもしれないと__delattr__(self, name)あなたが誤って記述した場合ということAnimals.DOG = CAT、それは静かに成功しません。
Joonas Pulakka、2011年

15
@shahjapan:興味深いが、比較的遅い:テストはのようなアクセスごとに行われますAnimals.DOG。また、constatsの値は文字列であるため、これらの定数との比較は、たとえば整数が値として許可されている場合よりも遅くなります。
エリックOレビゴット

3
@shahjapan:この解決策は、たとえばAlexandruやMarkの短い解決策ほど読みにくいと私は主張します。ただし、これは興味深い解決策です。:)
エリックOレビゴット

メソッドをオーバーライドする代わりに、メソッドsetattr()内の関数を使用してみました。これは同じように機能すると想定されています:クラスEnum(オブジェクト):def __init __(self、enum_string_list):if type(enum_string_list)== list:for enum_string in enum_string_list:setattr(self、enum_string、enum_string)else:raise AttributeError__init__()__getattr__()
Harshith JV

8
@AndréTerra:try-exceptブロック内のセットメンバーシップを確認するにはどうすればよいですか?
bgusach 2015年

210

数値が必要な場合は、以下が最も簡単な方法です。

dog, cat, rabbit = range(3)

Python 3.xでは、最後にスター付きのプレースホルダーを追加することもできます。これにより、メモリの浪費を気にせず、カウントできない場合に備えて、範囲の残りすべての値が吸収されます。

dog, cat, rabbit, horse, *_ = range(100)

1
しかし、これはより多くのメモリを必要とするかもしれません!
MJ

Pythonがアンパックする値の数をチェックするので、スター付きのプレースホルダーのポイントはわかりません(そのため、カウントが行われます)。
Gabriel Devillers

@GabrielDevillers、タプルの要素数に不一致がある場合、Pythonは例外を発生させると思います。
マークハリソン

1
実際、これは私のテスト(Python2、3)で行われますが、これは、プログラマーからのカウントの間違いが最初のテストでキャッチされることを意味します(正しいカウントを示すメッセージが表示されます)。
Gabriel Devillers

1
数えられない。スター付きのプレースホルダーは私の財政も修正できますか?
javadba

131

あなたにとっての最良の解決策は、あなたがあなたの偽物 から何を必要とするかによるでしょうenum

単純な列挙:

異なるアイテムを識別する名前のenumリストだけが必要な場合は、Mark Harrison(上記)による解決策が最適です。

Pen, Pencil, Eraser = range(0, 3)

rangeまた、aを使用すると、任意の開始値を設定できます

Pen, Pencil, Eraser = range(9, 12)

上記に加えて、アイテムがなんらかのコンテナに属している必要がある場合は、クラスに埋め込みます。

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)

enumアイテムを使用するには、コンテナー名とアイテム名を使用する必要があります。

stype = Stationery.Pen

複雑な列挙型:

enumの長いリストまたはより複雑なenumの使用の場合、これらのソリューションでは不十分です。Python Cookbookで公開されている、Python列挙シミュレートするためのWill Wareのレシピをご覧ください。オンライン版はこちらから入手できます

より詳しい情報:

PEP 354:Pythonの列挙型には、Pythonの列挙型に関する提案の興味深い詳細と、拒否された理由が含まれています。


7
range、それが0だ場合は、最初の引数を省略することができます
ToonAlfrink

いくつかの目的に合う別の偽の列挙型はmy_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))です。次にmy_enum、ルックアップで使用できます。たとえば、my_enum['Item0']シーケンスへのインデックスにすることができます。str.split重複がある場合に例外をスローする関数で結果をラップすることができます。
Ana Nimbus 2018年

いいね!FlagsでできることFlag1, Flag2, Flag3 = [2**i for i in range(3)]
majkelx

これが最良の答えです
ユーリーポズニアック

78

JDK 5より前のJavaで使用されていたタイプセーフな列挙型パターンには、いくつかの利点があります。Alexandruの答えと同じように、クラスを作成し、クラスレベルのフィールドは列挙値です。ただし、列挙値は小さな整数ではなくクラスのインスタンスです。これには、列挙値が誤って小さい整数と比較されないという利点があり、それらがどのように出力されるかを制御し、有用であれば任意のメソッドを追加し、isinstanceを使用してアサーションを作成できます。

class Animal:
   def __init__(self, name):
       self.name = name

   def __str__(self):
       return self.name

   def __repr__(self):
       return "<Animal: %s>" % self

Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")

>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False

python-devの最近のスレッドは、次のようないくつかの列挙型ライブラリが実際に存在することを指摘しています。


16
これは非常に悪いアプローチだと思います。Animal.DOG = Animal( "dog")Animal.DOG2 = Animal( "dog")assert Animal.DOG == Animal.DOG2は失敗します...
混乱

11
@Confusionユーザーはコンストラクターを呼び出すことは想定されていません。コンストラクターさえあるという事実は実装の詳細であり、新しい列挙値を作成しても意味がなく、既存のコードでは終了しないことをコードを使用している人に伝える必要があります。 "正しいことをします"。もちろん、これはAnimal.from_name( "dog")-> Animal.DOGの実装を妨げるものではありません。
Aaron Maenpaa、2009

13
「列挙値がうっかり小さな整数と比較しないという利点」これの利点は何ですか?列挙型を整数と比較することの何が問題になっていますか?特にenumをデータベースに格納する場合、通常は整数として格納する必要があるため、ある時点でそれを整数と比較する必要があります。
ibz

3
@アーロン・マエンパー。正しい。それはまだ壊れており、非常に複雑な方法です。
aaronasterling

4
@AaronMcSmoothそれは、「列挙型はいくつかのintの単なる名前」というCの観点から入ってくるのか、それとも列挙値が実際のオブジェクトでありメソッドを持つ(つまりJavaの列挙型である)よりオブジェクト指向のアプローチに依存するのかによって異なります。 1.5あり、型セーフな列挙型パターンが対象でした)。個人的には、switchステートメントは好きではないので、実際のオブジェクトである列挙値に傾いています。
Aaron Maenpaa

61

Enumクラスはワンライナーにすることができます。

class Enum(tuple): __getattr__ = tuple.index

使用方法(正引きと逆引き、キー、値、アイテムなど)

>>> State = Enum(['Unclaimed', 'Claimed'])
>>> State.Claimed
1
>>> State[1]
'Claimed'
>>> State
('Unclaimed', 'Claimed')
>>> range(len(State))
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]
[('Unclaimed', 0), ('Claimed', 1)]

私はそれが最も単純な目的で最もエレガントなソリューションだと思います。Python 2.4(はい、古いレガシーサーバー)では、タプルはインデックスを作成しません。リストに置き換えて解決しました。
Massimo

私はJupyterノートでこれを試して、それが1行の定義として機能しないことを発見したが、置くことGETATTR秒で定義すると、(インデント)ラインが受け入れられます。
user5920660 2017

この解決策では、inキーワードを使用して、きちんとしたメンバーを検索します。使用例:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Farzad Abdolhosseini

51

だから、同意する。Pythonでタイプセーフを強制しないでください。ただし、愚かな間違いから身を守りたいと思います。では、これについてどう思いますか?

class Animal(object):
    values = ['Horse','Dog','Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

これにより、列挙型を定義するときに値の衝突が発生しなくなります。

>>> Animal.Cat
2

もう1つの便利な利点があります。本当に高速な逆ルックアップです。

def name_of(self, i):
    return self.values[i]

私はこれが好きですが、タプルで効率を上げるために値を固定することもできますか?私はそれをいじって、initの argsからself.valuesを設定するバージョンを思いつきました。宣言できるのはうれしいですAnimal = Enum('horse', 'dog', 'cat')。私はまた、中とValueErrorをキャッチGETATTR self.valuesで欠品の場合には-代わりに、指定された名前の文字列ではAttributeErrorを高めるために良さそうです。その分野での限られた知識に基づいて、Python 2.7でメタクラスを機能させることはできませんでしたが、カスタムEnumクラスは、ストレートインスタンスメソッドで正常に機能します。
trojjer 2013年

49

Pythonにはに相当する機能が組み込まれておらずenum、他の回答には独自のものを実装するためのアイデアがあります(Pythonクックブックの上位バージョンに興味があるかもしれません)。

ただし、enumCでが呼び出される状況では、通常は単純な文字列のみを使用します。オブジェクト/属性の実装方法により、(C)Pythonはとにかく短い文字列で非常に高速に動作するように最適化されているため、整数を使用することによるパフォーマンス上のメリットはありません。タイプミス/無効な値を防ぐために、選択した場所にチェックを挿入できます。

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(クラスを使用する場合と比較した1つの欠点は、オートコンプリートの利点を失うことです)


2
私はこの解決策を好みます。可能な場合は組み込み型を使用するのが好きです。
Seun Osewa 2009

そのバージョンは本当に上にありません。多くのテストコードが提供されているだけです
Casebash、2009年

1
実際、「正しい」バージョンはコメントにあり、はるかに複雑です-メインバージョンには小さなバグがあります。
Casebash 2009年

39

2013-05-10、GuidoはPEP 435をPython 3.4標準ライブラリに受け入れることに同意しました。つまり、Pythonには列挙型のサポートが組み込まれています。

Python 3.3、3.2、3.1、2.7、2.6、2.5、および2.4で利用可能なバックポートがあります。enum34としてPypiにあります。

宣言:

>>> from enum import Enum
>>> class Color(Enum):
...     red = 1
...     green = 2
...     blue = 3

表現:

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

反復:

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

プログラムによるアクセス:

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue

詳細については、提案を参照しください。公式のドキュメントはまもなく続くでしょう。


33

私は次のようにPythonで列挙型を定義することを好みます:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

整数が一意であることを確認する必要がないので、整数を使用するよりもバグに強くなります(たとえば、Dog = 1とCat = 1と言った場合、ねじ込まれます)。

タイプミスを気にする必要がないため、文字列を使用するよりもバグに強くなります(たとえば、x == "catt"は警告なしに失敗しますが、x == Animal.Cattはランタイム例外です)。


31
def M_add_class_attribs(attribs):
    def foo(name, bases, dict_):
        for v, k in attribs:
            dict_[k] = v
        return type(name, bases, dict_)
    return foo

def enum(*names):
    class Foo(object):
        __metaclass__ = M_add_class_attribs(enumerate(names))
        def __setattr__(self, name, value):  # this makes it read-only
            raise NotImplementedError
    return Foo()

次のように使用します。

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError

一意の記号だけが必要で値を気にしない場合は、次の行を置き換えます。

__metaclass__ = M_add_class_attribs(enumerate(names))

これとともに:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)

11
私見これに変更enum(names)した方がきれいですenum(*names)-それを呼び出すときに余分な括弧を削除できます。
Chris Lutz、

私はこのアプローチが好きです。実際に変更して、属性値を名前と同じ文字列に設定しました。これには、Animal.DOG == 'DOG'という素晴らしいプロパティがあり、自動的に文字列化されます。(デバッグ出力を印刷するために非常に役立ちます。)
Ted Mielczarek

23

Python 3.4以降、列挙型の公式サポートが提供されます。ドキュメントと例については、Python 3.4ドキュメントページをご覧ください

列挙型はクラス構文を使用して作成されるため、読み書きが容易になります。別の作成方法については、機能APIで説明しています。列挙を定義するには、次のようにEnumをサブクラス化します。

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

バックポーティングもサポートされるようになりました。これが方法です。
2014年

22

うーん...列挙型に最も近いものは、次のように定義された辞書だと思います:

months = {
    'January': 1,
    'February': 2,
    ...
}

または

months = dict(
    January=1,
    February=2,
    ...
)

その後、次のようにして定数のシンボル名を使用できます。

mymonth = months['January']

タプルのリストやタプルのタプルのような他のオプションがありますが、値にアクセスするための「記号的」(定数文字列)の方法を提供するのは辞書だけです。

編集:私はアレクサンドルの答えも好きです!


そして何よりも、文字列値をコンボボックスアイテムとして表示する必要がある場合など、その値にアクセスする必要がある場合は、辞書を簡単に反復できます。代わりに、列挙型の代わりに辞書を使用してください。
LEMUEL ADANE、2012

22

Pythonでの列挙型の別の非常にシンプルな実装namedtuple

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

または、代わりに

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())

上記のサブクラスのメソッドと同様にset、これにより次のことが可能になります。

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO

ただし、キーと値が異なる可能性があるため、柔軟性が高くなります。これにより

MyEnum.FOO < MyEnum.BAR

連続番号の値を埋めるバージョンを使用する場合、期待どおりに動作します。


20

私が使用するもの:

class Enum(object):
    def __init__(self, names, separator=None):
        self.names = names.split(separator)
        for value, name in enumerate(self.names):
            setattr(self, name.upper(), value)
    def tuples(self):
        return tuple(enumerate(self.names))

使い方:

>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))

したがって、state.PUBLISHEDのような整数定数と、Djangoモデルの選択肢として使用する2つのタプルが提供されます。


17

davidgは、dictsの使用を推奨しています。さらに一歩進んで、セットを使用します。

months = set('January', 'February', ..., 'December')

これで、値が次のようにセット内のいずれかの値と一致するかどうかをテストできます。

if m in months:

ただし、dFのように、通常は列挙型の代わりに文字列定数を使用します。


うん、セットを継承してgetattrメソッドを提供すればはるかに良い!
shahjapan 2010年

17

複雑にしないでおく:

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList

    def __getattr__(self, name):
            return self.tupleList.index(name)

次に:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1

16

これは私が見た中で最高のものです:「Pythonのファーストクラス列挙型」

http://code.activestate.com/recipes/413486/

それはあなたにクラスを与え、そしてクラスはすべての列挙を含んでいます。列挙型は互いに比較できますが、特定の値はありません。それらを整数値として使用することはできません。(整数値であるC列挙に慣れているので、最初はこれに抵抗しました。しかし、整数として使用できない場合、誤って整数として使用できないので、全体的には勝つと思います。)各列挙型は一意の値です。enumを出力したり、それらを繰り返し処理したり、enum値がenum内にあることをテストしたりできます。それはかなり完全で滑らかです。

編集(cfi):上記のリンクはPython 3互換ではありません。これがPython 3へのenum.pyの移植です。

def cmp(a,b):
   if a < b: return -1
   if b < a: return 1
   return 0


def Enum(*names):
   ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!

   class EnumClass(object):
      __slots__ = names
      def __iter__(self):        return iter(constants)
      def __len__(self):         return len(constants)
      def __getitem__(self, i):  return constants[i]
      def __repr__(self):        return 'Enum' + str(names)
      def __str__(self):         return 'enum ' + str(constants)

   class EnumValue(object):
      __slots__ = ('__value')
      def __init__(self, value): self.__value = value
      Value = property(lambda self: self.__value)
      EnumType = property(lambda self: EnumType)
      def __hash__(self):        return hash(self.__value)
      def __cmp__(self, other):
         # C fans might want to remove the following assertion
         # to make all enums comparable by ordinal value {;))
         assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
         return cmp(self.__value, other.__value)
      def __lt__(self, other):   return self.__cmp__(other) < 0
      def __eq__(self, other):   return self.__cmp__(other) == 0
      def __invert__(self):      return constants[maximum - self.__value]
      def __nonzero__(self):     return bool(self.__value)
      def __repr__(self):        return str(names[self.__value])

   maximum = len(names) - 1
   constants = [None] * len(names)
   for i, each in enumerate(names):
      val = EnumValue(i)
      setattr(EnumClass, each, val)
      constants[i] = val
   constants = tuple(constants)
   EnumType = EnumClass()
   return EnumType


if __name__ == '__main__':
   print( '\n*** Enum Demo ***')
   print( '--- Days of week ---')
   Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
   print( Days)
   print( Days.Mo)
   print( Days.Fr)
   print( Days.Mo < Days.Fr)
   print( list(Days))
   for each in Days:
      print( 'Day:', each)
   print( '--- Yes/No ---')
   Confirmation = Enum('No', 'Yes')
   answer = Confirmation.No
   print( 'Your answer is not', ~answer)

このレシピは、拒否されたPEPの基礎として使用されました。 python.org/dev/peps/pep-0354 私が気に入っている拡張機能の1つ:enum値には、内部整数値を取得できるメンバー変数が必要です。列挙型を誤って整数にキャストすることはできないはずなので、.__int__()メソッドは列挙型の例外を発生させる必要があります。しかし、価値を引き出す方法があるはずです。また、クラス定義時に特定の整数値を設定できるようにする必要があるため、statモジュールの定数などに列挙型を使用でき ます。
steveha

14

バイナリファイル形式をデコードするために、Enumクラスが必要になることがありました。私がたまたま欲しかった機能は、簡潔な列挙型の定義、整数値または文字列のいずれかで列挙型のインスタンスを自由に作成できること、そして便利なrepr構文です。これが私が終わったものです:

>>> class Enum(int):
...     def __new__(cls, value):
...         if isinstance(value, str):
...             return getattr(cls, value)
...         elif isinstance(value, int):
...             return cls.__index[value]
...     def __str__(self): return self.__name
...     def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
...     class __metaclass__(type):
...         def __new__(mcls, name, bases, attrs):
...             attrs['__slots__'] = ['_Enum__name']
...             cls = type.__new__(mcls, name, bases, attrs)
...             cls._Enum__index = _index = {}
...             for base in reversed(bases):
...                 if hasattr(base, '_Enum__index'):
...                     _index.update(base._Enum__index)
...             # create all of the instances of the new class
...             for attr in attrs.keys():
...                 value = attrs[attr]
...                 if isinstance(value, int):
...                     evalue = int.__new__(cls, value)
...                     evalue._Enum__name = attr
...                     _index[value] = evalue
...                     setattr(cls, attr, evalue)
...             return cls
... 

それを使用する気まぐれな例:

>>> class Citrus(Enum):
...     Lemon = 1
...     Lime = 2
... 
>>> Citrus.Lemon
Citrus.Lemon
>>> 
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
...     Apple = 3
...     Banana = 4
... 
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True

主な機能:

  • str()int()およびrepr()すべてが可能な限り最も有用な出力をそれぞれ生成します。それぞれ、列挙の名前、その整数値、および列挙に評価されるPython式です。
  • コンストラクターによって返される列挙値は、定義済みの値に厳密に制限され、偶発的な列挙値はありません。
  • 列挙値はシングルトンです。彼らはと厳密に比較することができますis

enumを簡単に定義できるように、独自のメタクラスを持つスーパークラスを使用するのが本当に好きです。ここで欠けているのは__contains__メソッドです。特定の変数が列挙型の一部であることを確認できるようにしたいのですが、これは主に、関数パラメーターの許容値の列挙型が必要だからです。
xorsyst 2012

これは実際には、私が最初に作成したもの(ここで見つけることができます:enum_strict.py)vのわずかにトリミングされたバージョンであり、__instancecheck__メソッドを定義します。クラスはインスタンスのコレクションではないので1 in Fruit、ばかげています。ただし、リンクされたバージョンはisinstance(1, Fruit)、クラスとインスタンスの概念の点でどちらがより正しいかをサポートします。
SingleNegationElimination 2012

しかし、クラスを忘れて列挙型の観点から考えると、それらをコレクションとして考えることは理にかなっています。たとえば、ファイルを開くモードの列挙(MODE.OPEN、MODE.WRITEなど)があるとします。関数のパラメーターを確認したい: MODEのモードの場合:isintance(mode、Mode)
xorsyst


12

Pythonの新しい標準はPEP 435であるため、EnumクラスはPythonの将来のバージョンで使用できるようになります。

>>> from enum import Enum

ただし、今すぐ使用するには、PEPの動機となった元のライブラリをインストールできます。

$ pip install flufl.enum

次に、そのオンラインガイドに従って使用できます

>>> from flufl.enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue

10
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

名前を付けると問題になりますが、値を作成する代わりにオブジェクトを作成しないと、次のようになります。

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

ここにある他の実装を使用する場合(この例で名前付きインスタンスを使用する場合も)、異なる列挙型のオブジェクトを比較しないでください。ここに考えられる落とし穴があります:

>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True

うわぁ!


9

私は本当にアレック・トーマスのソリューションが好きです(http://stackoverflow.com/a/1695250):

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)

エレガントで見た目はきれいですが、指定された属性を持つクラスを作成する関数にすぎません。

関数に少し変更を加えると、もう少し「列挙型」として機能させることができます。

注:pygtkの新しいスタイル「列挙型」(Gtk.MessageType.WARNINGなど)の動作を再現しようとして、次の例を作成しました

def enum_base(t, **enums):
    '''enums with a base class'''
    T = type('Enum', (t,), {})
    for key,val in enums.items():
        setattr(T, key, T(val))

    return T

これは、指定されたタイプに基づいて列挙型を作成します。前の関数のように属性アクセスを与えることに加えて、型に関してEnumが期待するように動作します。また、基本クラスを継承します。

たとえば、整数の列挙:

>>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3)
>>> Numbers.ONE
1
>>> x = Numbers.TWO
>>> 10 + x
12
>>> type(Numbers)
<type 'type'>
>>> type(Numbers.ONE)
<class 'Enum'>
>>> isinstance(x, Numbers)
True

このメソッドで実行できるもう1つの興味深いことは、組み込みメソッドをオーバーライドして特定の動作をカスタマイズすることです。

def enum_repr(t, **enums):
    '''enums with a base class and repr() output'''
    class Enum(t):
        def __repr__(self):
            return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)

    for key,val in enums.items():
        i = Enum(val)
        i._name = key
        setattr(Enum, key, i)

    return Enum



>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)
>>> repr(Numbers.ONE)
'<enum ONE of type Enum(int)>'
>>> str(Numbers.ONE)
'1'

この「ベース」タイプのアイデアはすっきりしています:)
MestreLion 2013年

ええ、新しいPython 3.4 Enumでこれを行うこともできます:python.org/dev/peps/pep-0435/#other-derived-enumerations
bj0

7

PyPIのenumパッケージは、enumの堅牢な実装を提供します。以前の回答ではPEP 354について言及されていました。これは拒否されましたが、提案はhttp://pypi.python.org/pypi/enumに実装されました 。

使い方は簡単でエレガントです:

>>> from enum import Enum
>>> Colors = Enum('red', 'blue', 'green')
>>> shirt_color = Colors.green
>>> shirt_color = Colors[2]
>>> shirt_color > Colors.red
True
>>> shirt_color.index
2
>>> str(shirt_color)
'green'

5

列挙型にクラス定数を使用するというアレクサンドルの提案は非常にうまく機能します。

また、定数セットごとに辞書を追加して、人間が読める文字列表現を検索することも好きです。

これには2つの目的があります。a)enumをきれいに出力する簡単な方法を提供し、b)辞書は定数を論理的にグループ化して、メンバーシップをテストできるようにします。

class Animal:    
  TYPE_DOG = 1
  TYPE_CAT = 2

  type2str = {
    TYPE_DOG: "dog",
    TYPE_CAT: "cat"
  }

  def __init__(self, type_):
    assert type_ in self.type2str.keys()
    self._type = type_

  def __repr__(self):
    return "<%s type=%s>" % (
        self.__class__.__name__, self.type2str[self._type].upper())

5

ここに私が価値があると思ういくつかの異なる特徴を持つアプローチがあります:

  • 字句順ではなく列挙型の順序に基づく>および<比較を許可します
  • 名前、プロパティ、またはインデックスで項目をアドレス指定できます:xa、x ['a']またはx [0]
  • [:]や[-1]などのスライス操作をサポート

そして最も重要なのは、異なるタイプの列挙型間の比較を防ぐことです!

http://code.activestate.com/recipes/413486-first-class-enums-in-pythonに密接に基づいています

ここに含まれている多くのdoctestは、このアプローチの違いを示しています。

def enum(*names):
    """
SYNOPSIS
    Well-behaved enumerated type, easier than creating custom classes

DESCRIPTION
    Create a custom type that implements an enumeration.  Similar in concept
    to a C enum but with some additional capabilities and protections.  See
    http://code.activestate.com/recipes/413486-first-class-enums-in-python/.

PARAMETERS
    names       Ordered list of names.  The order in which names are given
                will be the sort order in the enum type.  Duplicate names
                are not allowed.  Unicode names are mapped to ASCII.

RETURNS
    Object of type enum, with the input names and the enumerated values.

EXAMPLES
    >>> letters = enum('a','e','i','o','u','b','c','y','z')
    >>> letters.a < letters.e
    True

    ## index by property
    >>> letters.a
    a

    ## index by position
    >>> letters[0]
    a

    ## index by name, helpful for bridging string inputs to enum
    >>> letters['a']
    a

    ## sorting by order in the enum() create, not character value
    >>> letters.u < letters.b
    True

    ## normal slicing operations available
    >>> letters[-1]
    z

    ## error since there are not 100 items in enum
    >>> letters[99]
    Traceback (most recent call last):
        ...
    IndexError: tuple index out of range

    ## error since name does not exist in enum
    >>> letters['ggg']
    Traceback (most recent call last):
        ...
    ValueError: tuple.index(x): x not in tuple

    ## enums must be named using valid Python identifiers
    >>> numbers = enum(1,2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: Enum values must be string or unicode

    >>> a = enum('-a','-b')
    Traceback (most recent call last):
        ...
    TypeError: Error when calling the metaclass bases
        __slots__ must be identifiers

    ## create another enum
    >>> tags = enum('a','b','c')
    >>> tags.a
    a
    >>> letters.a
    a

    ## can't compare values from different enums
    >>> letters.a == tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    >>> letters.a < tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    ## can't update enum after create
    >>> letters.a = 'x'
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'a' is read-only

    ## can't update enum after create
    >>> del letters.u
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'u' is read-only

    ## can't have non-unique enum values
    >>> x = enum('a','b','c','a')
    Traceback (most recent call last):
        ...
    AssertionError: Enums must not repeat values

    ## can't have zero enum values
    >>> x = enum()
    Traceback (most recent call last):
        ...
    AssertionError: Empty enums are not supported

    ## can't have enum values that look like special function names
    ## since these could collide and lead to non-obvious errors
    >>> x = enum('a','b','c','__cmp__')
    Traceback (most recent call last):
        ...
    AssertionError: Enum values beginning with __ are not supported

LIMITATIONS
    Enum values of unicode type are not preserved, mapped to ASCII instead.

    """
    ## must have at least one enum value
    assert names, 'Empty enums are not supported'
    ## enum values must be strings
    assert len([i for i in names if not isinstance(i, types.StringTypes) and not \
        isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'
    ## enum values must not collide with special function names
    assert len([i for i in names if i.startswith("__")]) == 0,\
        'Enum values beginning with __ are not supported'
    ## each enum value must be unique from all others
    assert names == uniquify(names), 'Enums must not repeat values'

    class EnumClass(object):
        """ See parent function for explanation """

        __slots__ = names

        def __iter__(self):
            return iter(constants)

        def __len__(self):
            return len(constants)

        def __getitem__(self, i):
            ## this makes xx['name'] possible
            if isinstance(i, types.StringTypes):
                i = names.index(i)
            ## handles the more normal xx[0]
            return constants[i]

        def __repr__(self):
            return 'enum' + str(names)

        def __str__(self):
            return 'enum ' + str(constants)

        def index(self, i):
            return names.index(i)

    class EnumValue(object):
        """ See parent function for explanation """

        __slots__ = ('__value')

        def __init__(self, value):
            self.__value = value

        value = property(lambda self: self.__value)

        enumtype = property(lambda self: enumtype)

        def __hash__(self):
            return hash(self.__value)

        def __cmp__(self, other):
            assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'
            return cmp(self.value, other.value)

        def __invert__(self):
            return constants[maximum - self.value]

        def __nonzero__(self):
            ## return bool(self.value)
            ## Original code led to bool(x[0])==False, not correct
            return True

        def __repr__(self):
            return str(names[self.value])

    maximum = len(names) - 1
    constants = [None] * len(names)
    for i, each in enumerate(names):
        val = EnumValue(i)
        setattr(EnumClass, each, val)
        constants[i] = val
    constants = tuple(constants)
    enumtype = EnumClass()
    return enumtype


3

このソリューションは、リストとして定義された列挙型のクラスを取得する簡単な方法です(これ以上煩わしい整数の割り当てはありません)。

enumeration.py:

import new

def create(class_name, names):
    return new.classobj(
        class_name, (object,), dict((y, x) for x, y in enumerate(names))
    )

example.py:

import enumeration

Colors = enumeration.create('Colors', (
    'red',
    'orange',
    'yellow',
    'green',
    'blue',
    'violet',
))

2
これは、昔ながらのクラス作成方法です。type(class_name, (object,), dict(...))代わりに単に使用しないのはなぜですか?
ターミナルの

3

元の列挙型の提案であるPEP 354は何年も前に却下されましたが、引き続き復活しています。ある種の列挙型は3.2に追加される予定でしたが、3.3に戻されて忘れられました。そして今、Python 3.4に含めることを目的としたPEP 435があります。PEP 435のリファレンス実装はflufl.enumです。

2013年4月の時点で、3.4の標準ライブラリに何かを追加する必要があるという一般的な合意があるようです。ただし、「何か」が何であるかについて人々が同意できる場合に限ります。それは難しい部分です。ここここから始まるスレッド、および2013年の初めの数か月間のその他のスレッドをご覧ください。

一方、これが発生するたびに、PyPI、ActiveStateなどに多数の新しい設計と実装が表示されるため、FLUFL設計が気に入らない場合は、PyPI検索を試してください。


3

以下を使用してください。

TYPE = {'EAN13':   u'EAN-13',
        'CODE39':  u'Code 39',
        'CODE128': u'Code 128',
        'i25':     u'Interleaved 2 of 5',}

>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']

私はDjangoモデルの選択にそれを使用しました、そしてそれは非常にPythonicに見えます。これは実際にはEnumではありませんが、機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.