python-3.x intenum用法 - 我如何在Python中表示'Enum'?





2.7 python2 (25)


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

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

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

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

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

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




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



我非常喜歡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'



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'



我使用的是:

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模型中用作選擇的二元組。




如果您需要數字值,以下是最快的方法:

dog, cat, rabbit = range(3)

在Python 3.x中,您還可以在末尾添加一個帶星號的佔位符,這將吸收範圍的所有剩餘值,以防您不介意浪費內存並且無法計數:

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



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中的First Class Enums”

它給你一個類,而這個類包含所有的枚舉。 枚舉可以相互比較,但沒有任何特別的價值; 你不能將它們用作整數值。 (我之所以拒絕這個,是因為我習慣C枚舉,它是整數值,但是如果你不能用它作為一個整數,你不能把它當作一個整數來錯誤的使用,所以我認為這是一個勝利)每個枚舉都是唯一的值。 你可以打印枚舉,你可以迭代它們,你可以測試一個枚舉值是“在”枚舉。 它非常完整和光滑。

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



所以,我同意。 讓我們不要在Python中強制實現類型安全,但我想保護自己免受愚蠢的錯誤。 那麼我們對此有何看法?

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



亞歷山德魯建議使用類常量進行枚舉的工作很好。

我還想為每組常量添加一個字典來查找可讀的字符串表示。

這有兩個目的: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())



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

枚舉是獨一無二的,它們可以用'is'代替'=='進行測試,它們在我的代碼中佔用很小的概念,並且很容易導入到客戶端代碼中。他們不支持任何花哨的str()行為,但是到目前為止,這在YAGNI類中。




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最近的一篇文章指出,在野外有兩個枚舉庫,包括:




在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 3.4開始,官方會支持枚舉。 您可以在Python 3.4文檔頁面上找到文檔和示例。

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

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



這個解決方案是一個簡單的方法來獲得定義為列表的枚舉類(不再有惱人的整數賦值):

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



這是一個實現:

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

這是它的用法:

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

print(Animals.DOG)



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



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



把事情簡單化:

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



嗯......我想最接近枚舉的是一個字典,定義如下:

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

要么

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

然後,你可以使用像這樣的常量的符號名稱:

mymonth = months['January']

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

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




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'



為了解碼二進製文件格式,我有機會需要一個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



{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

對於在兩個詞典('b')中都帶有鍵的項目,您可以通過將最後一個鍵入到輸出中來控制哪一個最終出現在輸出中。





python python-3.x enums