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

我如何在Python中表示等价的Enum ?


当前回答

这个解决方案是获取定义为列表的枚举类的简单方法(没有更多烦人的整数赋值):

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

其他回答

Python 2.7和find_name()

下面是所选思想的一个易于阅读的实现,其中包含一些辅助方法,这些方法可能比“reverse_mapping”更python化,使用起来更简洁。要求Python >= 2.7。

为了解决下面的一些注释,枚举对于防止代码中的拼写错误非常有用,例如对于状态机,错误分类器等。

def Enum(*sequential, **named):
  """Generate a new enum type. Usage example:

  ErrorClass = Enum('STOP','GO')
  print ErrorClass.find_name(ErrorClass.STOP)
    = "STOP"
  print ErrorClass.find_val("STOP")
    = 0
  ErrorClass.FOO     # Raises AttributeError
  """
  enums = { v:k for k,v in enumerate(sequential) } if not named else named

  @classmethod
  def find_name(cls, val):
    result = [ k for k,v in cls.__dict__.iteritems() if v == val ]
    if not len(result):
        raise ValueError("Value %s not found in Enum" % val)
    return result[0]

  @classmethod
  def find_val(cls, n):
    return getattr(cls, n)

  enums['find_val'] = find_val
  enums['find_name'] = find_name
  return type('Enum', (), enums)

最好的解决方案取决于你需要从你的假枚举中得到什么。

简单的枚举:

如果你只需要枚举作为标识不同项目的名称列表,Mark Harrison(上图)的解决方案是很棒的:

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

使用范围还允许你设置任何起始值:

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

除此之外,如果你还要求项属于某种类型的容器,那么将它们嵌入到一个类中:

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

要使用枚举项,你现在需要使用容器名和项名:

stype = Stationery.Pen

复杂的枚举:

对于枚举的长列表或更复杂的enum使用,这些解决方案是不够的。你可以参考Will Ware在Python Cookbook中发布的关于在Python中模拟枚举的食谱。这里有一个在线版本。

更多信息:

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

在Java pre-JDK 5中使用的类型安全枚举模式有一个 优势的数量。就像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上最近的一个线程指出,在野外有几个枚举库,包括:

flufl.enum lazr.enum ... 和富有想象力的enum

def enum( *names ):

    '''
    Makes enum.
    Usage:
        E = enum( 'YOUR', 'KEYS', 'HERE' )
        print( E.HERE )
    '''

    class Enum():
        pass
    for index, name in enumerate( names ):
        setattr( Enum, name, index )
    return Enum

另一个非常简单的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())




# Example for dictionary param:
values = {"Salad": 20, "Carrot": 99, "Tomato": "No i'm not"} 
Vegetables= enum(**values)

# >>> print(Vegetables.Tomato)        'No i'm not'


# Example for keyworded params: 
Fruits = enum(Apple="Steve Jobs", Peach=1, Banana=2)

# >>> print(Fruits.Apple)             'Steve Jobs'

就像上面子类设置的方法一样,这允许:

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

但是具有更大的灵活性,因为它可以有不同的键和值。这允许

MyEnum.FOO < MyEnum.BAR

如果使用填充连续数字值的版本,则按预期操作。