質問

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

Python で Enum と同等のものを表現するにはどうすればよいですか?

役に立ちましたか?

解決

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

より高度な Enum テクニックについては、以下を試してください。 アエナム図書館 (2.7、3.3+、同じ著者 enum34. 。コードは py2 と py3 の間で完全には互換性がありません。あなたは必要になるでしょう __order__ Python2で).

  • 使用するには 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'

他のヒント

PEP 435 より前の Python には同等のものはありませんでしたが、独自のものを実装できました。

私自身は、シンプルにするのが好きです (ネット上で恐ろしく複雑な例をいくつか見たことがあります)。次のようなものです...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4 では (PEP 435)、作ることができます 列挙型 基本クラス。これにより、PEP で説明されている追加機能が少し得られます。たとえば、列挙型メンバーは整数とは異なり、次のもので構成されます。 name そして 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

ここに実装の 1 つを示します。

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

その使用法は次のとおりです。

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

print(Animals.DOG)

数値が必要な場合は、最も簡単な方法を次に示します。

dog, cat, rabbit = range(3)

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

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

あなたにとって最適なソリューションは、お客様が何を要求しているかによって異なります。 偽物 enum.

単純な列挙型:

必要な場合は、 enum 単なるリストとして 名前 異なるものを識別する アイテム, 、による解決策 マーク・ハリソン (上) は素晴らしいです:

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

を使って range 任意に設定することもできます 開始値:

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

上記に加えて、アイテムが 容器 何らかの種類のコードをクラスに埋め込みます。

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

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

stype = Stationery.Pen

複雑な列挙型:

enum の長いリストや enum のより複雑な使用の場合、これらの解決策では十分ではありません。Will War のレシピを参照してください。 Python での列挙型のシミュレーション に掲載されました Python クックブック. 。そのオンライン版が利用可能です ここ.

より詳しい情報:

PEP 354:Python の列挙型 Python での enum の提案とそれが拒否された理由の興味深い詳細が記載されています。

Java Pre-JDK 5で使用されたTypeSafe Enumパターンには、多くの利点があります。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 のスレッド 以下を含むいくつかの enum ライブラリが世に出ていると指摘しました。

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 には同等の機能が組み込まれていません enum, 、および他の回答には、独自の実装のアイデアがあります( オーバートップバージョン Python クックブックに記載されています)。

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

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

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

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

それで、私も同意します。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]

2013 年 5 月 10 日、グイドは受け入れることに同意しました。 PEP 435 Python 3.4 標準ライブラリに追加します。これは、Python についに列挙型のサポートが組み込まれたことを意味します。

Python 3.3、3.2、3.1、2.7、2.6、2.5、および 2.4 で使用できるバックポートがあります。それはPypiにあります enum34.

宣言:

>>> 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

詳細については、以下を参照してください。 提案. 。公式ドキュメントはおそらくすぐに続くでしょう。

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

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

整数が一意であるかどうかを心配する必要がないため、整数を使用するよりもバグが起こりにくくなります。Dog = 1 と Cat = 1 と言ったら、あなたは台無しになるでしょう)。

タイプミスを心配する必要がないため、文字列を使用するよりもバグが起こりにくくなります。x == "catt" はサイレントに失敗しますが、x == Animal.Catt は実行時例外です)。

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)

うーん...enum に最も近いものは次のように定義される辞書だと思います。

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

または

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

次に、次のように定数のシンボリック名を使用できます。

mymonth = months['January']

タプルのリストやタプルのタプルなどの他のオプションがありますが、辞書は、値にアクセスする「シンボリック」(定数文字列)の方法を提供する唯一のものです。

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

もう 1 つの非常に単純な 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

連続した数値を入力するバージョンを使用すると、期待どおりに動作します。

私が使っているもの:

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 つのタプルが得られます。

Python 3.4 からは、列挙型が正式にサポートされる予定です。ドキュメントと例を見つけることができます ここのPython 3.4ドキュメントページ.

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

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

davidg は dict の使用をお勧めします。私はさらに一歩進んでセットを使用します。

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

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

if m in months:

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

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

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

これによりクラスが提供され、そのクラスにはすべての列挙型が含まれます。列挙型は相互に比較できますが、特定の値はありません。これらを整数値として使用することはできません。(整数値である C 列挙型に慣れているため、最初はこれに抵抗しました。ただし、整数として使用できない場合は、間違って整数として使用することはできないので、全体的には勝利だと思います。) 各列挙型は一意の値です。列挙型を出力したり、列挙型を反復処理したり、列挙型値が列挙型の「中に」あるかどうかをテストしたりできます。かなり完成度が高く、滑らかです。

編集 (cfi):上記のリンクは Python 3 と互換性がありません。enum.py を Python 3 に移植したものは次のとおりです。

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)

単純にする:

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

バイナリ ファイル形式をデコードする目的で、Enum クラスが必要になることがあります。私がたまたま欲しかった機能は、簡潔な enum 定義、整数値または文字列のいずれかで 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

私は Alec Thomas のソリューション (http://stackoverflow.com/a/1695250) がとても気に入っています。

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

これはエレガントですっきりとした見た目ですが、指定された属性を持つクラスを作成する単なる関数です。

関数を少し変更すると、関数をもう少し「列挙型」に動作させることができます。

注記:Pygtkの新しいスタイルの「Enums」の動作を再現しようとすることで、次の例を作成しました(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'
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

うわぁ!

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

enum パッケージの PyPI 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'

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

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

これには次の 2 つの目的があります。a) 列挙型を整形して表示する簡単な方法を提供し、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())

私が価値があると考える、いくつかの異なる特徴を備えたアプローチを次に示します。

  • 字句順ではなく列挙型の順序に基づいて > と < の比較が可能になります
  • 項目を名前、プロパティ、またはインデックスで指定できます。x.a、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

ここにバリエーションがあります アレック・トーマスの解決策:

def enum(*args, **kwargs):
    return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) 

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1

この解決策は、リストとして定義された列挙型のクラスを取得する簡単な方法です (面倒な整数の代入は不要です)。

列挙.py:

import new

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

例.py:

import enumeration

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

元の enum 提案では、 PEP354, 、何年も前に拒否されましたが、何度も戻ってきます。ある種の列挙型は 3.2 に追加される予定でしたが、3.3 に延期され、その後忘れられました。そして今、 PEP 435 Python 3.4 に組み込まれることを目的としています。PEP 435 のリファレンス実装は次のとおりです。 flufl.enum.

2013 年 4 月の時点では、次のような一般的なコンセンサスがあるようです。 何か 人々がその「何か」が何であるべきかについて同意できる限り、3.4 の標準ライブラリに追加されるべきです。そこが難しいところです。スレッドが開始されていることを確認してください ここ そして ここ, 、および 2013 年の初めの数か月間で他にも 6 個のスレッドが作成されました。

一方、これが登場するたびに、多数の新しい設計や実装が PyPI や ActiveState などに登場するため、FLUFL 設計が気に入らない場合は、 PyPI検索.

バリアント (列挙値の名前の取得をサポート) アレック・トーマスの見事な答え:

class EnumBase(type):
    def __init__(self, name, base, fields):
        super(EnumBase, self).__init__(name, base, fields)
        self.__mapping = dict((v, k) for k, v in fields.iteritems())
    def __getitem__(self, val):
        return self.__mapping[val]

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

Numbers = enum(ONE=1, TWO=2, THREE='three')
print Numbers.TWO
print Numbers[Numbers.ONE]
print Numbers[2]
print Numbers['three']
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top