我怎样才能在Python中表示一个'Enum'?

我主要是一个C#开发人员,但我目前正在使用Python进行一个项目。

我怎么能代表一个Python的枚举相当于?

如PEP 435中所述,Enum已经被添加到Python 3.4中。 它也已经支持到 3.3,3.2,3.1,2.7,2.6,2.5 和2.4 pypi。

对于更高级的枚举技术,试试aenum库 (2.7,3.3+,和enum34一样的作者),py2和py3之间的代码是不完全兼容的,例如你需要__order__中的__order__

  • 要使用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 

在早期版本中,完成枚举的一种方法是:

 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没有相同的function,但是你可以实现你自己的。

我自己,我喜欢保持简单(我已经看到networking上一些可怕的复杂的例子),像这样的…

 class Animal: DOG = 1 CAT = 2 x = Animal.DOG 

在Python 3.4( PEP 435 )中,可以使Enum成为基类。 这可以让你获得一些额外的function,在PEP中描述。 例如,枚举值不同于整数。

 class Animal(Enum): DOG = 1 CAT = 2 print(Animal.DOG) <Animal.DOG: 1> 

如果您不想input值,请使用以下快捷方式:

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

这是一个实现:

 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) 

对你来说最好的解决scheme将取决于你从你的 enum需要什么。

简单的枚举:

如果你需要enum作为唯一标识不同项目名字列表, Mark Harrison (上面)的解决scheme是非常棒的:

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

使用range还允许您设置任何起始值

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

除了上面的内容,如果你还要求这些物品属于某种容器 ,那么将它们embedded到一个类中:

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

要使用枚举项目,您现在需要使用容器名称和项目名称:

 stype = Stationery.Pen 

复杂的枚举:

对于枚举的长列表或更复杂的枚举使用,这些解决scheme是不够的。 你可以看看Will Cook在Python Cookbook中发布的模拟Enumerations的方法 。 在线版本可以在这里find 。

更多信息:

PEP 354:Python中的枚举有一个关于Python的枚举提案的有趣的细节,以及为什么它被拒绝。

Java JDK 5以前的types安全枚举模式有许多优点。 就像亚历山德鲁的回答一样,你创build一个类和类级别字段是枚举值; 然而,枚举值是类的实例,而不是小整数。 这有一个好处,就是你的枚举值不会无意中和小整数比较,你可以控制它们的打印方式,如果有用的话可以添加任意方法,并使用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最近的一个线程指出,在野外有两个枚举库,包括:

  • flufl.enum
  • lazr.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食谱中的顶级版本感兴趣)。

但是,在C语言中需要使用enum情况下,我通常最终只使用简单的string :由于实现对象/属性的方式,(C)Python对短string的工作速度进行了优化,所以在那里使用整数不会有任何性能上的好处。 为了防止input错误/无效值,您可以在选定的位置插入检查。

 ANIMALS = ['cat', 'dog', 'python'] def take_for_a_walk(animal): assert animal in ANIMALS ... 

(与使用类相比,一个缺点是您失去了自动完成的好处)

所以,我同意。 让我们不要在Python中强制实现types安全,但我想保护自己免受愚蠢的错误。 那么我们怎么看待这个呢?

 class Animal(object): values = ['Horse','Dog','Cat'] class __metaclass__(type): def __getattr__(self, name): return self.values.index(name) 

它使我在定义枚举时不受价值冲突的影响。

 >>> Animal.Cat 2 

还有一个便利的优势:真正快速的反向查找:

 def name_of(self, i): return self.values[i] 
 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) 

我更喜欢在Python中定义枚举,如下所示:

 class Animal: class Dog: pass class Cat: pass x = Animal.Dog 

这比使用整数更具有防错性,因为您不必担心确保整数是唯一的(例如,如果您说Dog = 1,Cat = 1,您将被拧紧)。

因为你不必担心input错误(比如x ==“catt”默默地失败,但是x == Animal.Catt是一个运行时exception),所以它比使用string更具有缺陷性。

在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的backport。 它是在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中使用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 

如果您使用填充顺序号码值的版本,则按预期行事。

嗯…我想最接近一个枚举将是一个字典,定义如下:

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

要么

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

然后,你可以像这样使用常量的符号名称:

 mymonth = months['January'] 

还有其他选项,比如元组列表或者元组元组,但是字典是唯一一个为您提供“符号”(常量string)方式来访问值的方法。

编辑:我也喜欢Alexandru的回答!

我使用的是:

 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模型中的select。

davidgbuild议使用字典。 我会更进一步,使用集合:

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

现在你可以testing一个值是否与这个集合中的一个值相匹配:

 if m in months: 

像dF,但是,我通常只是使用string常量代替枚举。

这是我见过的最好的一个:“Python中的First Class Enums”

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

它给你一个类,而这个类包含所有的枚举。 枚举可以相互比较,但没有任何特殊的价值; 您不能将它们用作整数值。 (我之所以拒绝这个,是因为我习惯了C枚举,它是整型值,但是如果你不能把它作为一个整数使用,你不能把它作为一个整数,所以我认为这是一个整体)每个枚举是一个独特的值。 你可以打印枚举,你可以迭代它们,你可以testing一个枚举值是“在”枚举。 这是相当完整和光滑的。

编辑(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) 

为了解码二进制文件格式,我有机会需要Enum类。 我碰巧想要的function是简明的枚举定义,通过整数值或string自由创build枚举实例的能力,以及有用的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()都可以产生最有用的输出,分别是enumartion的名字,整数值和返回枚举的Pythonexpression式。
  • 由构造函数返回的枚举值严格限制为预定义的值,而不是意外的枚举值。
  • 枚举值是单例; 他们可以严格比较is

从Python 3.4开始,官方会支持枚举。 您可以在Python 3.4文档页面上find文档和示例。

枚举是使用类语法创build的,这使得它们易于读取和写入。 Functional API中描述了另一种创build方法。 要定义一个枚举,Enum的子类如下:

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

把事情简单化:

 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 

我真的很喜欢Alec Thomas的解决scheme(http://stackoverflow.com/a/1695250):;

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

它看起来既优雅又干净,但它只是一个创build具有指定属性的类的函数。

通过对函数进行一些修改,我们可以使它更加“活跃”:

注意:我通过尝试重现pygtk新样式“枚举”的行为(如Gtk.MessageType.WARNING)创build了以下示例:

 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 

这将创build一个基于指定types的枚举。 除了像前一个函数一样赋予属性访问外,它的行为方式与您期望的Enum相对于types相同。 它也inheritance了基类。

例如,整数枚举:

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

这个方法可以做的另一个有趣的事情是通过重写内置方法来自定义特定的行为:

 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' 

Python中的新标准是PEP 435 ,所以Enum类将在未来的Python版本中可用:

 >>> from enum import Enum 

不过现在开始使用它可以安装激发PEP的原始库 :

 #sudo pip install flufl.enum //or #sudo easy_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) 

如果你命名它,是你的问题,但是如果不创build对象而不是值允许你这样做:

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

哎呀!

Alexandrubuild议使用类常量来枚举运行相当好。

我也想为每一组常量添加一个字典来查找一个可读的string表示。

这有两个目的:a)它提供了一个简单的方法来漂亮地打印你的枚举和b)字典在逻辑上对常量进行分组,以便testing成员资格。

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

PyPI的枚举包提供了一个强大的枚举实现。 较早的答案提到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' 

This solution is a simple way of getting a class for the enumeration defined as a list (no more annoying integer assignments):

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', )) 

While the original enum proposal, PEP 354 , was rejected years ago, it keeps coming back up. Some kind of enum was intended to be added to 3.2, but it got pushed back to 3.3 and then forgotten. And now there's a PEP 435 intended for inclusion in Python 3.4. The reference implementation of PEP 435 is flufl.enum .

As of April 2013, there seems to be a general consensus that something should be added to the standard library in 3.4—as long as people can agree on what that "something" should be. That's the hard part. See the threads starting here and here , and a half dozen other threads in the early months of 2013.

Meanwhile, every time this comes up, a slew of new designs and implementations appear on PyPI, ActiveState, etc., so if you don't like the FLUFL design, try a PyPI search .

Here's an approach with some different characteristics I find valuable:

  • allows > and < comparison based on order in enum, not lexical order
  • can address item by name, property or index: xa, x['a'] or x[0]
  • supports slicing operations like [:] or [-1]

and most importantly prevents comparisons between enums of different types !

Based closely on http://code.activestate.com/recipes/413486-first-class-enums-in-python .

Many doctests included here to illustrate what's different about this approach.

 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 

I had need of some symbolic constants in pyparsing to represent left and right associativity of binary operators. I used class constants like this:

 # an internal class, not intended to be seen by client code class _Constants(object): pass # an enumeration of constants for operator associativity opAssoc = _Constants() opAssoc.LEFT = object() opAssoc.RIGHT = object() 

Now when client code wants to use these constants, they can import the entire enum using:

 import opAssoc from pyparsing 

The enumerations are unique, they can be tested with 'is' instead of '==', they don't take up a big footprint in my code for a minor concept, and they are easily imported into the client code. They don't support any fancy str() behavior, but so far that is in the YAGNI category.

Here is a variant on Alec Thomas's solution :

 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