python2 - python intenum用法




我如何在Python中表示'Enum'? (20)

JDK 5以前版本中使用的類型安全枚舉模式具有許多優點。 就像亞歷山德魯的回答一樣,你創建一個類和類級別字段是枚舉值; 然而,枚舉值是類的實例,而不是小整數。 這樣做的好處是你的枚舉值不會無意中等於小整數,你可以控制它們的打印方式,如果有用的話添加任意方法並使用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最近的一篇文章指出,在野外有兩個枚舉庫,包括:

我主要是一名C#開發人員,但我目前正在使用Python開發一個項目。

我如何表示Python中Enum的等價物?


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

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'

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

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 .


一個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實現枚舉,使用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']

還有其他選項,如元組列表或元組元組,但字典是唯一一個為您提供“符號”(常量字符串)方式來訪問該值的方法。

編輯:我也喜歡Alexandru的回答!


在PEP 435之前,Python沒有相同的功能,但可以實現自己的功能。

我自己,我喜歡保持簡單(我在網上看到過一些非常複雜的例子),像這樣的...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

在Python 3.4( PEP 435 )中,可以使Enum成為基類。 這可以讓你獲得一些額外的功能,如PEP所述。 例如,枚舉值不同於整數。

class Animal(Enum):
    DOG = 1
    CAT = 2

print(Animal.DOG)
<Animal.DOG: 1>

如果您不想輸入值,請使用以下快捷方式:

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

PEP 435中所述,Enum已經被添加到Python 3.4中。 它在pypi上也回到3.3,3.2,3.1,2.7,2.6,2.5 和2.4

對於更高級的枚舉技術,嘗試aenum庫 (2.7,3.3+,與enum34相同的作者。代碼在py2和py3之間不完全兼容,例如你需要python 2中的__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'

對你來說最好的解決方案將取決於你從 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

複雜枚舉:

對於枚舉的長列表或更複雜的枚舉使用,這些解決方案是不夠的。 您可以看看Will Cook在Python Cookbook中發布的用於模擬Python枚舉的配方。 在線版本可以here

更多信息:

PEP 354:Python中的枚舉具有Python中enum提議的有趣細節,以及為什麼它被拒絕。


從Python 3.4開始,官方會支持枚舉。 您可以在Python 3.4文檔頁面上找到文檔和示例。

枚舉是使用類語法創建的,這使得它們易於讀取和寫入。 Functional API中介紹了另一種創建方法。 要定義枚舉,Enum的子類如下所示:

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

我更喜歡在Python中定義枚舉,如下所示:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

這比使用整數更具有防錯性,因為您不必擔心確保整數是唯一的(例如,如果您說Dog = 1,Cat = 1,您會被擰緊)。

它比使用字符串更具有缺陷性,因為你不必擔心輸入錯誤(例如,x ==“catt”默默地失敗,但x == Animal.Catt是一個運行時異常)。


我非常喜歡Alec Thomas的解決方案(http://.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'

把事情簡單化:

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類。 我碰巧想要的功能是簡明的枚舉定義,通過整數值或字符串自由創建枚舉實例的能力以及有用的表示。 這就是我最終的結果:

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

這是亞歷克托馬斯解決方案的一個變種:

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

這是一個實現:

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

這是它的用法:

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

print(Animals.DOG)

達維建議使用字典。 我會更進一步,並使用集合:

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

現在您可以測試某個值是否與此集合中的某個值匹配,如下所示:

if m in months:

像dF,但是,我通常只是使用字符串常量來代替枚舉。


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)




enums