首页 文章

如何在Python中表示'Enum'?

提问于
浏览
1146

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

我怎样才能在Python中表示Enum的等价物?

30 回答

  • 30

    以下是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
    
  • 121

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

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

    它比使用整数更容易出错,因为你不必担心确保整数是唯一的(例如,如果你说Dog = 1而Cat = 1你就会被搞砸) .

    它比使用字符串更容易出错,因为您不必担心拼写错误(例如x ==“catt”无声地失败,但x == Animal.Catt是运行时异常) .

  • 4

    PEP 435中所述,枚举已添加到Python 3.4中 . 在pypi上它也是backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4 .

    对于更高级的Enum技术,请尝试aenum library(2.7,3.3,同一作者为 enum34 . 代码在py2和py3之间不完全兼容,例如,您需要order in 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
    

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

    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'
    
  • 32

    把事情简单化:

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

    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)]
    
  • 2426

    这是一种具有一些我认为有 Value 的不同特征的方法:

    • 允许>和<基于枚举顺序的比较,而不是词法顺序

    • 可以按名称,属性或索引寻址项:x.a,x ['a']或x [0]

    • 支持像[:]或[-1]这样的切片操作

    最重要的 prevents comparisons between enums of different types

    基于http://code.activestate.com/recipes/413486-first-class-enums-in-python密切关注 .

    这里包含许多文档,用于说明这种方法的不同之处 .

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

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

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

  • 2

    davidg建议使用dicts . 我更进一步,使用集合:

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

    现在,您可以测试值是否与集合中的某个值匹配,如下所示:

    if m in months:
    

    但是,就像dF一样,我通常只使用字符串常量来代替枚举 .

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

    哎呀!

  • 16

    我需要在pyparsing中使用一些符号常量来表示二元运算符的左右相关性 . 我使用了这样的类常量:

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

    现在,当客户端代码想要使用这些常量时,他们可以使用以下命令导入整个枚举:

    import opAssoc from pyparsing
    

    枚举是唯一的,它们可以使用'is'而不是'=='进行测试,它们不支持任何花哨的str()行为,但到目前为止在YAGNI类别中 .

  • 303

    从Python 3.4开始,官方将支持枚举 . 您可以找到文档和示例here on Python 3.4 documentation page .

    枚举是使用类语法创建的,这使它们易于读写 . Functional API中描述了另一种创建方法 . 要定义枚举,请将子类Enum如下:

    from enum import Enum
    class Color(Enum):
         red = 1
         green = 2
         blue = 3
    
  • 13
    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)
    
  • 7

    虽然最初的枚举提案,PEP 354,在多年前被拒绝,但它一直在回升 . 某种枚举旨在添加到3.2中,但它被推回到3.3然后被遗忘 . 现在有一个PEP 435打算包含在Python 3.4中 . PEP 435的参考实现是flufl.enum .

    截至2013年4月,似乎已达成普遍共识,即只要人们能够就_88384应该是什么达成一致,就应该在3.4中添加标准库 . 这是困难的部分 . 查看2013年初几个月开始_88385和here以及其他六个主题的主题 .

    同时,每次出现这种情况时,PyPI,ActiveState等都会出现大量新设计和实现,所以如果您不喜欢FLUFL设计,请尝试PyPI search .

  • 9

    最适合您的解决方案取决于您对假货的要求 enum .

    Simple enum:

    如果您只需要 enum 作为识别不同项目的名称列表, Mark Harrison (上面)的解决方案很棒:

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

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

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

    除上述内容外,如果您还要求项目属于某种容器,则将它们嵌入到类中:

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

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

    stype = Stationery.Pen
    

    Complex enum:

    对于冗长的枚举列表或更复杂的枚举用法,这些解决方案是不够的 . 您可以通过Python Cookbook中发布的Will Ware for Simulate Enumerations查看配方 . 该在线版本可用here .

    More info:

    PEP 354: Enumerations in Python有关于Python中枚举的提议的有趣细节以及它被拒绝的原因 .

  • 744

    在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有一个后端口 . 它在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
    

    有关更多信息,请参阅the proposal . 官方文档很快就会发布 .

  • 21

    这是一个实现:

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

    这是它的用法:

    Animals = Enum(["DOG", "CAT", "HORSE"])
    
    print(Animals.DOG)
    
  • 46

    来自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'
    
  • 189

    这个解决方案是一种简单的方法,可以将枚举的类定义为一个列表(不再烦人的整数赋值):

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

    Alexandru建议在枚举中使用类常量非常有效 .

    我还想为每组常量添加一个字典来查找人类可读的字符串表示 .

    这有两个目的: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())
    
  • 16

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

    最近thread on python-dev指出,野外有几个枚举库,包括:

  • 46

    嗯...我想最接近枚举的是字典,定义如下:

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

    要么

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

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

    mymonth = months['January']
    

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

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

  • 31

    我非常喜欢Alec Thomas的解决方案(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
    

    使用此方法可以完成的另一个有趣的事情是通过覆盖内置方法来自定义特定行为:

    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'
    
  • 18

    如果您需要数值,这是最快的方法:

    dog, cat, rabbit = range(3)
    

    在Python 3.x中,您还可以在末尾添加一个带星号的占位符,这将占用该范围的所有剩余值,以防您不介意浪费内存并且无法计算:

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

    所以,我同意 . 我们不要在Python中强制执行类型安全,但我想保护自己免受愚蠢的错误 . 那么我们怎么看待这个呢?

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

    在定义我的枚举时,它让我免于 Value 冲突 .

    >>> Animal.Cat
    2
    

    还有另一个方便的优点:真正快速的反向查找:

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

    Python中的新标准是PEP 435,因此未来的Python版本中将提供Enum类:

    >>> from enum import Enum
    

    但是现在开始使用它你可以安装激励PEP的original library

    #sudo pip install flufl.enum   //or #sudo easy_install flufl.enum
    

    然后你can use it as per its online guide

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

    这是我见过的最好的:“Python中的一流枚举”

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

    它为您提供了一个类,该类包含所有枚举 . 枚举可以相互比较,但没有任何特定的 Value ;你不能将它们用作整数值 . (我一开始就反对这个,因为我习惯于C枚举,它是整数值 . 但是如果你不能将它用作整数,你就不能错误地将它用作整数,所以总体来说我认为这是一个胜利 . )每个枚举都是唯一的值 . 你可以打印枚举,你可以迭代它们,你可以测试枚举值是否在枚举中“ . 它非常完整和光滑 .

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

    在PEP 435之前,Python没有等效的,但你可以实现自己的 .

    我自己,我喜欢保持简单(我在网上看过一些非常复杂的例子),像这样......

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

    在Python 3.4(PEP 435)中,您可以使Enum成为基类 . 这将为您提供一些额外的功能,如PEP中所述 . 例如,枚举成员与整数不同,它们由 namevalue 组成 .

    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 实施can be converted to lists and are iterable . 其成员的顺序是声明顺序,与其值无关 . 例如:

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

    我用的是什么:

    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模型中用作选项的两元组 .

  • 59

    为了解码二进制文件格式,我有机会需要一个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() 都产生了最有用的输出,分别是enumartion的名称,它的整数值,以及一个回溯到枚举的Python表达式 .

    • 构造函数返回的枚举值严格限制为预定义值,不包含偶然的枚举值 .

    • 枚举值是单例;他们可以严格地与 is 进行比较

  • 6

    Python没有内置的等效于 enum ,其他答案也有实现自己的想法(你可能也对Python食谱中的over the top version感兴趣) .

    但是,在C中调用 enum 的情况下,我通常会结束 just using simple strings :由于实现了对象/属性的方式,(C)Python已经过优化,无论如何都可以使用短字符串快速工作,所以不会使用整数真的有任何性能优势 . 为了防止打字错误/无效值,您可以在选定位置插入支票 .

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

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

相关问题