Вопрос

В основном я разработчик на C #, но в настоящее время работаю над проектом на Python.

Как я могу представить эквивалент перечисления в Python?

Это было полезно?

Решение

Перечисления были добавлены в Python 3.4, как описано в БОДРОСТЬ ДУХА 435.Это также было перенесено в 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, и 2.4 на pypi.

Для получения более продвинутых методов перечисления попробуйте библиотека aenum (2.7, 3.3+, тот же автор, что и enum34.Код не совсем совместим между py2 и py3, напримервам понадобится __order__ в 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'

Другие советы

До PEP 435 у Python не было эквивалента, но вы могли бы реализовать свой собственный.

Мне нравится, чтобы все было просто (я видел несколько ужасно сложных примеров в сети), что-то вроде этого ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG
<Ч>

В Python 3.4 ( PEP 435 ) вы можете сделать Enum базовый класс. Это дает вам немного дополнительной функциональности, описанной в PEP. Например, члены enum отличаются от целых и состоят из name и value.

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 реализации можно преобразовать в списки и повторяемы . Порядок его членов является порядком декларации и не имеет ничего общего с их значениями. Например:

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

Вот одна из реализаций:

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)

Лучшее решение для вас будет зависеть от того, что вам требуется от фальшивки enum .

Простое перечисление:

Если вам нужен range как список имен , идентифицирующих различные элементы , решение Марка Харрисона (выше) отлично:

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

Использование <=> также позволяет вам установить любое начальное значение :

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

В дополнение к вышесказанному, если вам также требуется, чтобы элементы принадлежали к контейнеру какого-либо вида, то вставьте их в класс:

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

Чтобы использовать элемент enum, вам нужно будет использовать имя контейнера и имя элемента:

stype = Stationery.Pen

Сложное перечисление:

Для длинных списков enum или более сложных вариантов использования enum этих решений будет недостаточно. Вы можете посмотреть рецепт Уилла Уэра для Моделирования перечислений в Python , опубликованного в Python Cookbook . Онлайн-версия этого доступна здесь .

Дополнительная информация:

PEP 354: перечисления в Python есть интересные детали предложения enum в Python и почему оно было отклонено.

Типобезопасный шаблон перечисления, который использовался в Java до JDK 5, имеет ряд преимуществ.Как и в ответе Александру, вы создаете класс поля уровня класса - это значения enum;однако значения enum являются экземплярами класса, а не небольшими целыми числами.Это имеет преимущество в том, что ваши значения enum случайно не сравниваются равными для небольших целых чисел вы можете контролировать способ их печати, добавлять произвольные методы, если это полезно, и создавать утверждения с помощью 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 указал, что в дикой природе существует пара библиотек перечислений, в том числе:

Класс 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 требуется <=>, я обычно заканчиваю просто использованием простых строк : из-за того, как объекты / атрибуты являются реализован, (C) Python в любом случае оптимизирован для очень быстрой работы с короткими строками, так что при использовании целых чисел не будет никакого выигрыша в производительности. Для защиты от опечаток / недействительных значений вы можете вставить проверки в выбранных местах.

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(Одним из недостатков по сравнению с использованием класса является то, что вы теряете преимущество автозаполнения)

Итак, я согласен. Давайте не будем обеспечивать безопасность типов в 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]

2013-05-10 Гвидо согласился принять БОДРОСТЬ ДУХА 435 в стандартную библиотеку Python 3.4.Это означает, что в Python наконец-то появилась встроенная поддержка перечислений!

Доступен бэкпорт для Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, и 2.4.Это есть в Pypi как перечисление34.

Декларация:

>>> 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 следующим образом:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

Это более устойчиво к ошибкам, чем использование целых чисел, поскольку вам не нужно беспокоиться о том, чтобы целые числа были уникальными (например, если вы сказали, что Dog = 1 и Cat = 1, вы были бы облажаны).

Это более надежно, чем использование строк, так как вам не нужно беспокоиться об опечатках (например, x == " catt " тихо завершается с ошибкой, но x == Animal.Catt является исключением времени выполнения).

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)

Хммм...Я полагаю, что ближайшей вещью к перечислению был бы словарь, определенный либо следующим образом:

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

или

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

Затем вы можете использовать символическое имя для констант следующим образом:

mymonth = months['January']

Существуют и другие варианты, такие как список кортежей или набор кортежей, но словарь - единственный, который предоставляет вам "символический" (постоянная строка) способ доступа к значению .

Редактировать:Мне тоже нравится ответ Александру!

Еще одна очень простая реализация перечисления в 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

действовать так, как ожидается, если вы используете версию, которая заполняет значения последовательных чисел.

Что я использую:

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.

Начиная с Python 3.4 будет официально поддерживаться перечисления. Документацию и примеры вы можете найти здесь, на странице документации по Python 3.4 .

  

Перечисления создаются с использованием синтаксиса класса, что упрощает их   читать и писать. Альтернативный метод создания описан в   Функциональный API. Чтобы определить перечисление, создайте подкласс Enum следующим образом:

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

davidg рекомендует использовать dicts. Я бы пошел на шаг дальше и использовал бы наборы:

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

Теперь вы можете проверить, соответствует ли значение одному из значений в наборе следующим образом:

if m in months:

как dF, я обычно просто использую строковые константы вместо перечислений.

Это лучшее, что я когда-либо видел:"Перечисления первого класса в Python"

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

Это дает вам класс, и этот класс содержит все перечисления.Перечисления можно сравнивать друг с другом, но они не имеют какой-либо конкретной ценности;вы не можете использовать их как целочисленное значение.(Сначала я сопротивлялся этому, потому что привык к перечислениям 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)

Пусть все будет просто:

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 с целью декодирования двоичного формата файла.Функции, которые мне понадобились, - это краткое определение перечисления, возможность свободно создавать экземпляры перечисления либо по целому значению, либо по строке, а также полезный 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() все они выдают максимально полезный вывод, соответственно, имя перечисления, его целочисленное значение и выражение Python, которое возвращает результат к перечислению.
  • Перечисленные значения, возвращаемые конструктором, строго ограничены предопределенными значениями, никаких случайных перечисляемых значений.
  • Перечисленные значения являются одиночными;их можно строго сравнить с is

Мне очень нравится решение Алекса Томаса (http://stackoverflow.com/a/1695250):

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

Это элегантный и чистый вид, но это просто функция, которая создает класс с указанными атрибутами.

С небольшой модификацией функции мы можем заставить ее действовать немного более "enumy":

  

ПРИМЕЧАНИЕ. Я создал следующие примеры, пытаясь воспроизвести   поведение нового стиля pygutk 'enums' (например, 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'
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

Фу!

Новый стандарт в Python - это PEP 435 , поэтому Enum класс будет доступен в следующих версиях Python:

>>> from enum import Enum

Однако, чтобы начать использовать его сейчас, вы можете установить оригинальная библиотека , которая мотивировала PEP:

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

Пакет enum из 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'

Предложение Александру об использовании констант класса для перечислений работает довольно хорошо.

Я также хотел бы добавить словарь для каждого набора констант для поиска удобочитаемого строкового представления.

Это служит двум целям: а) предоставляет простой способ красивой печати вашего перечисления и б) словарь логически группирует константы, чтобы вы могли проверить их на членство.

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

Вот подход с некоторыми другими характеристиками, которые я нахожу ценными:

  • позволяет > и < сравнение, основанное на порядке перечисления, а не на лексическом порядке
  • может обращаться к элементу по имени, свойству или индексу:x.a, x['a'] или x[0]
  • поддерживает операции нарезки, такие как [:] или [-1]

и самое главное предотвращает сравнение между перечислениями разных типов!

Тесно основанный на http://code.activestate.com/recipes/413486-first-class-enums-in-python.

Многие тесты doctest включены здесь, чтобы проиллюстрировать, в чем отличие этого подхода.

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

Вот вариант на Решение Алекса Томаса :

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

Это решение представляет собой простой способ получения класса для перечисления, определенного как список (больше никаких раздражающих целочисленных назначений).:

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

Хотя первоначальное предложение enum, PEP 354 , было отклонено несколько лет назад Снова возвращается. Какой-то enum был предназначен для добавления в 3.2, но он был перенесен в 3.3, а затем забыт. А теперь есть PEP 435 , предназначенный для включения в Python 3.4. Ссылочная реализация PEP 435 - flufl.enum .

По состоянию на апрель 2013 года, по-видимому, существует общее мнение о том, что что-то должно быть добавлено в стандартную библиотеку в 3.4 & # 8212, при условии, что люди могут договориться о том, что это Quot;! что-то <> Quot; должно быть. Это сложная часть. Смотрите темы, начиная с здесь и здесь , а также полдюжины других тем в первые месяцы 2013 года.

Между тем, каждый раз, когда это происходит, появляется множество новых проектов и реализаций в PyPI, ActiveState и т. д., поэтому, если вам не нравится дизайн FLUFL, попробуйте PyPI search .

Вариант (с поддержкой получения имени значения перечисления) для Аккуратный ответ Алека Томаса :

class EnumBase(type):
    def __init__(self, name, base, fields):
        super(EnumBase, self).__init__(name, base, fields)
        self.__mapping = dict((v, k) for k, v in fields.iteritems())
    def __getitem__(self, val):
        return self.__mapping[val]

def enum(*seq, **named):
    enums = dict(zip(seq, range(len(seq))), **named)
    return EnumBase('Enum', (), enums)

Numbers = enum(ONE=1, TWO=2, THREE='three')
print Numbers.TWO
print Numbers[Numbers.ONE]
print Numbers[2]
print Numbers['three']
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top