回答:
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'
**named
古いバージョンのための列挙機能で)カスタム値をサポートすることです:enum("blue", "red", "green", black=0)
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
object()
。
ここに1つの実装があります:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
これがその使用法です:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
__setattr__(self, name, value)
かもしれないと__delattr__(self, name)
あなたが誤って記述した場合ということAnimals.DOG = CAT
、それは静かに成功しません。
Animals.DOG
。また、constatsの値は文字列であるため、これらの定数との比較は、たとえば整数が値として許可されている場合よりも遅くなります。
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__()
try-except
ブロック内のセットメンバーシップを確認するにはどうすればよいですか?
数値が必要な場合は、以下が最も簡単な方法です。
dog, cat, rabbit = range(3)
Python 3.xでは、最後にスター付きのプレースホルダーを追加することもできます。これにより、メモリの浪費を気にせず、カウントできない場合に備えて、範囲の残りすべての値が吸収されます。
dog, cat, rabbit, horse, *_ = range(100)
あなたにとっての最良の解決策は、あなたがあなたの偽物 から何を必要とするかによるでしょう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の列挙型に関する提案の興味深い詳細と、拒否された理由が含まれています。
range
、それが0だ場合は、最初の引数を省略することができます
my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
です。次にmy_enum
、ルックアップで使用できます。たとえば、my_enum['Item0']
シーケンスへのインデックスにすることができます。str.split
重複がある場合に例外をスローする関数で結果をラップすることができます。
Flag1, Flag2, Flag3 = [2**i for i in range(3)]
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の最近のスレッドは、次のようないくつかの列挙型ライブラリが実際に存在することを指摘しています。
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)]
in
キーワードを使用して、きちんとしたメンバーを検索します。使用例:'Claimed' in Enum(['Unclaimed', 'Claimed'])
だから、同意する。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]
Animal = Enum('horse', 'dog', 'cat')
。私はまた、中とValueErrorをキャッチGETATTR self.valuesで欠品の場合には-代わりに、指定された名前の文字列ではAttributeErrorを高めるために良さそうです。その分野での限られた知識に基づいて、Python 2.7でメタクラスを機能させることはできませんでしたが、カスタムEnumクラスは、ストレートインスタンスメソッドで正常に機能します。
Pythonにはに相当する機能が組み込まれておらずenum
、他の回答には独自のものを実装するためのアイデアがあります(Pythonクックブックの上位バージョンに興味があるかもしれません)。
ただし、enum
Cでが呼び出される状況では、通常は単純な文字列のみを使用します。オブジェクト/属性の実装方法により、(C)Pythonはとにかく短い文字列で非常に高速に動作するように最適化されているため、整数を使用することによるパフォーマンス上のメリットはありません。タイプミス/無効な値を防ぐために、選択した場所にチェックを挿入できます。
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(クラスを使用する場合と比較した1つの欠点は、オートコンプリートの利点を失うことです)
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
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(names)
した方がきれいですenum(*names)
-それを呼び出すときに余分な括弧を削除できます。
Python 3.4以降、列挙型の公式サポートが提供されます。ドキュメントと例については、Python 3.4ドキュメントページをご覧ください。
列挙型はクラス構文を使用して作成されるため、読み書きが容易になります。別の作成方法については、機能APIで説明しています。列挙を定義するには、次のようにEnumをサブクラス化します。
from enum import Enum
class Color(Enum):
red = 1
green = 2
blue = 3
うーん...列挙型に最も近いものは、次のように定義された辞書だと思います:
months = {
'January': 1,
'February': 2,
...
}
または
months = dict(
January=1,
February=2,
...
)
その後、次のようにして定数のシンボル名を使用できます。
mymonth = months['January']
タプルのリストやタプルのタプルのような他のオプションがありますが、値にアクセスするための「記号的」(定数文字列)の方法を提供するのは辞書だけです。
編集:私はアレクサンドルの答えも好きです!
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つのタプルが提供されます。
davidgは、dictsの使用を推奨しています。さらに一歩進んで、セットを使用します。
months = set('January', 'February', ..., 'December')
これで、値が次のようにセット内のいずれかの値と一致するかどうかをテストできます。
if m in months:
ただし、dFのように、通常は列挙型の代わりに文字列定数を使用します。
これは私が見た中で最高のものです:「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)
.__int__()
メソッドは列挙型の例外を発生させる必要があります。しかし、価値を引き出す方法があるはずです。また、クラス定義時に特定の整数値を設定できるようにする必要があるため、stat
モジュールの定数などに列挙型を使用でき ます。
バイナリファイル形式をデコードするために、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
__instancecheck__
メソッドを定義します。クラスはインスタンスのコレクションではないので1 in Fruit
、ばかげています。ただし、リンクされたバージョンはisinstance(1, Fruit)
、クラスとインスタンスの概念の点でどちらがより正しいかをサポートします。
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
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
うわぁ!
私は本当にアレック・トーマスのソリューションが好きです(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'
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'
列挙型にクラス定数を使用するというアレクサンドルの提案は非常にうまく機能します。
また、定数セットごとに辞書を追加して、人間が読める文字列表現を検索することも好きです。
これには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())
ここに私が価値があると思ういくつかの異なる特徴を持つアプローチがあります:
そして最も重要なのは、異なるタイプの列挙型間の比較を防ぐことです!
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
このソリューションは、リストとして定義された列挙型のクラスを取得する簡単な方法です(これ以上煩わしい整数の割り当てはありません)。
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',
))
type(class_name, (object,), dict(...))
代わりに単に使用しないのはなぜですか?
元の列挙型の提案であるPEP 354は何年も前に却下されましたが、引き続き復活しています。ある種の列挙型は3.2に追加される予定でしたが、3.3に戻されて忘れられました。そして今、Python 3.4に含めることを目的としたPEP 435があります。PEP 435のリファレンス実装はflufl.enum
です。
2013年4月の時点で、3.4の標準ライブラリに何かを追加する必要があるという一般的な合意があるようです。ただし、「何か」が何であるかについて人々が同意できる場合に限ります。それは難しい部分です。こことここから始まるスレッド、および2013年の初めの数か月間のその他のスレッドをご覧ください。
一方、これが発生するたびに、PyPI、ActiveStateなどに多数の新しい設計と実装が表示されるため、FLUFL設計が気に入らない場合は、PyPI検索を試してください。
以下を使用してください。
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ではありませんが、機能します。