Как я могу представить Enum в Python?

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

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1 143
0
975 365
43
Перейти к ответу Данный вопрос помечен как решенный

Ответы 43

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

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

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

В Python 3.4 (PEP 435) вы можете сделать Enum базовым классом. Это дает вам немного дополнительных функций, описанных в PEP. Например, члены перечисления отличаются от целых чисел и состоят из 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

Какой смысл определять числовые значения (1 и 2)? Они кажутся бесполезными, поэтому я предпочитаю решение зачерата.

bortzmeyer 26.09.2008 23:16

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

strager 03.02.2009 03:54

Сначала вы должны были создать экземпляр класса Animal .. X = Animal () z = X.DOG

aatifh 05.03.2009 18:38

Нет, это переменная класса.

Georg Schölly 05.03.2009 18:41

По умолчанию Python является динамическим. Нет веских причин для обеспечения безопасности во время компиляции на таком языке, как Python, особенно когда ее нет. И еще ... хороший паттерн хорош только в том контексте, в котором он был создан. Хороший шаблон также может быть заменен или полностью бесполезен, в зависимости от инструментов, которые вы используете.

Alexandru Nedelcu 21.07.2009 12:21

Как правило, лучше использовать диапазон

Casebash 25.10.2009 07:48

что, если мы наследуем set и используем метод getattr для использования Enum, см. stackoverflow.com/questions/36932/…

shahjapan 06.02.2010 06:15

Что, если у меня есть 100 значений, я должен написать числа для каждого из них? Юк!

L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ 07.05.2010 04:47

@Longpoke, если у вас 100 значений, то вы определенно делаете что-то не так;) Мне нравятся числа, связанные с моими перечислениями ... их легко записать (по сравнению со строками), их легко сохранить в базе данных и они совместимы с перечисление C / C++, которое упрощает маршалинг.

Alexandru Nedelcu 07.05.2010 16:04

Я использую это с заменой чисел на object().

Tobu 20.07.2010 11:53

X = object() неудобен, потому что он не знает, что это такое (вы можете сравнивать только с namespace.X), и рискованно, потому что copy.deepcopy () или сериализация / десериализация создает новый, который не равен ни одному из тех, которые вы определенный! Числа, по крайней мере, безопасны, но строки обычно лучше.

Beni Cherniavsky-Paskin 20.06.2012 15:19

Было бы лучше реализовать это со словарем, чтобы было легче сопоставить тип животного с его порядковым номером?

kdazzle 24.06.2012 11:49

Исходный PEP354 больше не просто отклоняется, а теперь помечается как замененный. PEP435 добавляет стандартный Enum для Python 3.4. См. python.org/dev/peps/pep-0435

Peter Hansen 10.05.2013 23:05

В этом ответе также следует упомянуть пакет flufl.enum и включить обратную компиляцию для python

Voo 13.08.2013 02:44

@AlexandruNedelcu, у всех, кто заходил на эту страницу в поисках ответа на этот вопрос, вероятно, есть веская причина для принудительного применения допустимых значений с помощью перечисления. Как насчет принудительного применения API, улучшения читабельности, улучшения интерфейса IDE или ограничения эффектов, когда значения констант изменяются между версиями?

shrewmouse 07.06.2017 18:35

Я использую этот стиль перечисления, потому что: а) удаление элемента не меняет нумерацию следующих элементов (важно, если значения сохраняются на диск); б) средства проверки статического типа (например: mypy) будут знать, что Animal.CAT является int, и отображать ошибки типа при неправильном использовании; c) вспомогательные данные могут храниться вместе со значениями (например, MAMMALS = {CAT, DOG} как другой атрибут класса).

Tyson 01.03.2018 11:27

Пример с class Animal(Enum) не будет работать из-за Cannot extend enumerations

dzieciou 18.05.2020 15:47

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

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

или же

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

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

mymonth = months['January']

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

Обновлено: мне тоже нравится ответ Александру!

И, прежде всего, вы можете легко выполнить итерацию по словарю, если вам нужно получить доступ к его значениям, например, чтобы его строковые значения отображались как элементы поля со списком. Поэтому вместо перечислений используйте словарь.

LEMUEL ADANE 12.02.2012 21:32

Python не имеет встроенного эквивалента enum, а в других ответах есть идеи для реализации вашего собственного (вас также может заинтересовать поверх верхней версии в кулинарной книге Python).

Однако в ситуациях, когда enum будет вызван в C, я обычно заканчиваю просто используя простые строки: из-за способа реализации объектов / атрибутов (C) Python в любом случае оптимизирован для очень быстрой работы с короткими строками, поэтому не было бы действительно будет какое-то преимущество в производительности при использовании целых чисел. Чтобы защититься от опечаток / неверных значений, вы можете вставлять чеки в выбранные места.

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

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

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

Я предпочитаю это решение. Мне нравится по возможности использовать встроенные типы.

Seun Osewa 03.02.2009 21:01

Эта версия на самом деле не является чрезмерной. Он просто имеет много поставляемого тестового кода

Casebash 25.10.2009 08:10

Собственно, «правильная» версия есть в комментариях и намного сложнее - в основной версии есть небольшая ошибка.

Casebash 25.10.2009 08:24

Если вам нужны числовые значения, вот самый быстрый способ:

dog, cat, rabbit = range(3)

В Python 3.x вы также можете добавить помеченный звездочкой заполнитель в конце, который поглотит все оставшиеся значения диапазона на случай, если вы не против тратить память и не можете считать:

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

Но для этого может потребоваться больше памяти!

M.J 28.02.2019 07:42

Я не вижу смысла помеченного звездочкой заполнителя, учитывая, что Python будет проверять количество значений для распаковки (поэтому он будет делать подсчет за вас).

Gabriel Devillers 16.09.2019 16:48

@GabrielDevillers, я думаю, Python вызовет исключение, если есть несоответствие в количестве элементов в кортеже для назначения.

Mark Harrison 17.09.2019 00:18

Действительно, в моем тесте (Python2,3) это означает, что любая ошибка подсчета со стороны программиста будет обнаружена в первом тесте (с сообщением, указывающим правильный счет).

Gabriel Devillers 17.09.2019 01:29

Я не умею считать. Может ли заполнитель, помеченный звездочкой, также исправить мои финансы?

StephenBoesch 01.11.2019 01:33

Также можно вставить это в class Enum и получить Enum.dog и т. д. Очень аккуратно. Не так хорошо, как Голанг, хотя.

Yuriy Pozniak 09.05.2020 05:52

Шаблон typeafe enum, который использовался в Java до 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 указал, что существует пара библиотек перечислений в дикой природе, в том числе:

Я считаю, что это очень плохой подход. Animal.DOG = Animal («собака») Animal.DOG2 = Animal («собака») assert Animal.DOG == Animal.DOG2 не работает ...

Confusion 19.12.2009 14:05

@Confusion Пользователь не должен вызывать конструктор, тот факт, что существует даже конструктор, является деталью реализации, и вы должны сообщить всем, кто когда-либо использует ваш код, что создание новых значений перечисления не имеет смысла и что выход из кода не будет "делать правильные вещи". Конечно, это не мешает вам реализовать Animal.from_name ("dog") -> Animal.DOG.

Aaron Maenpaa 21.12.2009 17:41

«преимущество в том, что значения вашего перечисления не сравниваются случайно с маленькими целыми числами» Какое в этом преимущество? Что плохого в сравнении вашего перечисления с целыми числами? Особенно, если вы храните перечисление в базе данных, вы обычно хотите, чтобы оно хранилось как целые числа, поэтому вам придется в какой-то момент сравнить его с целыми числами.

ibz 22.09.2010 00:23

@AaronMcSmooth Неправильно: dir (Animal) -> ['CAT', 'DOG', 'док', 'в этом', 'модуль', 'повтор', 'ул.']

Aaron Maenpaa 24.09.2010 20:19

@Aaaron Maenpaa. правильный. Это все еще сломанный и слишком сложный способ сделать это.

aaronasterling 24.09.2010 23:00

@AaronMcSmooth Это действительно зависит от того, входите ли вы с точки зрения C «Перечисления - это просто имена для пары целых чисел» или более объектно-ориентированного подхода, когда значения перечислений являются фактическими объектами и имеют методы (как перечисления в Java 1.5, и для которых собирался шаблон перечисления типа безопасного типа). Лично мне не нравятся операторы switch, поэтому я склоняюсь к значениям enum, которые являются реальными объектами.

Aaron Maenpaa 24.09.2010 23:31

Каким образом эта реализация паттерна типобезопасного перечисления позволяет каждому значению перечисления предоставлять свою собственную реализацию метода?

Nathan 24.07.2011 03:27

@Nathan Вы можете объявить (несвязанную) функцию, связать ее с получать, например, с объектом DOG, а затем добавить в качестве члена DOG. Или вы можете создать подкласс Animal для каждой константы, для которой нужен определенный метод.

Thiago Chaves 02.08.2012 00:06

Теперь попробуем сделать Flags.FLAG1 | Flags.FLAG2, ох, не работает!

LtWorf 07.01.2014 13:27

Я предпочитаю такой подход. символ в перечислении не является целым числом, мы просто используем целые числа в C, потому что, ну, это C. объект сравнения выполняется быстро, «или» можно легко реализовать,

LBarret 29.04.2016 16:40

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

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

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

if m in months:

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

да !, гораздо лучше, если вы наследуете set и предоставите метод getattr!

shahjapan 11.05.2010 07:10

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

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

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

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

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)

IMHO было бы чище, если бы вы изменили enum(names) на enum(*names) - тогда вы могли бы убрать лишнюю скобку при его вызове.

Chris Lutz 18.11.2009 05:56

Мне нравится такой подход. Я фактически изменил его, чтобы установить значение атрибута в ту же строку, что и имя, которое имеет приятное свойство Animal.DOG == 'DOG', поэтому они автоматически структурируют себя для вас. (Очень помогает при распечатке отладочной информации.)

Ted Mielczarek 28.05.2010 16:01

Забавно, мне это просто понадобилось на днях, и я не смог найти достойную реализацию ... поэтому я написал свою собственную:

import functools

class EnumValue(object):
    def __init__(self,name,value,type):
        self.__value=value
        self.__name=name
        self.Type=type
    def __str__(self):
        return self.__name
    def __repr__(self):#2.6 only... so change to what ever you need...
        return '{cls}({0!r},{1!r},{2})'.format(self.__name,self.__value,self.Type.__name__,cls=type(self).__name__)

    def __hash__(self):
        return hash(self.__value)
    def __nonzero__(self):
        return bool(self.__value)
    def __cmp__(self,other):
        if isinstance(other,EnumValue):
            return cmp(self.__value,other.__value)
        else:
            return cmp(self.__value,other)#hopefully their the same type... but who cares?
    def __or__(self,other):
        if other is None:
            return self
        elif type(self) is not type(other):
            raise TypeError()
        return EnumValue('{0.Name} | {1.Name}'.format(self,other),self.Value|other.Value,self.Type)
    def __and__(self,other):
        if other is None:
            return self
        elif type(self) is not type(other):
            raise TypeError()
        return EnumValue('{0.Name} & {1.Name}'.format(self,other),self.Value&other.Value,self.Type)
    def __contains__(self,other):
        if self.Value==other.Value:
            return True
        return bool(self&other)
    def __invert__(self):
        enumerables=self.Type.__enumerables__
        return functools.reduce(EnumValue.__or__,(enum for enum in enumerables.itervalues() if enum not in self))

    @property
    def Name(self):
        return self.__name

    @property
    def Value(self):
        return self.__value

class EnumMeta(type):
    @staticmethod
    def __addToReverseLookup(rev,value,newKeys,nextIter,force=True):
        if value in rev:
            forced,items=rev.get(value,(force,()) )
            if forced and force: #value was forced, so just append
                rev[value]=(True,items+newKeys)
            elif not forced:#move it to a new spot
                next=nextIter.next()
                EnumMeta.__addToReverseLookup(rev,next,items,nextIter,False)
                rev[value]=(force,newKeys)
            else: #not forcing this value
                next = nextIter.next()
                EnumMeta.__addToReverseLookup(rev,next,newKeys,nextIter,False)
                rev[value]=(force,newKeys)
        else:#set it and forget it
            rev[value]=(force,newKeys)
        return value

    def __init__(cls,name,bases,atts):
        classVars=vars(cls)
        enums = classVars.get('__enumerables__',None)
        nextIter = getattr(cls,'__nextitr__',itertools.count)()
        reverseLookup = {}
        values = {}

        if enums is not None:
            #build reverse lookup
            for item in enums:
                if isinstance(item,(tuple,list)):
                    items=list(item)
                    value=items.pop()
                    EnumMeta.__addToReverseLookup(reverseLookup,value,tuple(map(str,items)),nextIter)
                else:
                    value=nextIter.next()
                    value=EnumMeta.__addToReverseLookup(reverseLookup,value,(str(item),),nextIter,False)#add it to the reverse lookup, but don't force it to that value

            #build values and clean up reverse lookup
            for value,fkeys in reverseLookup.iteritems():
                f,keys=fkeys
                for key in keys:
                    enum=EnumValue(key,value,cls)
                    setattr(cls,key,enum)
                    values[key]=enum
                reverseLookup[value]=tuple(val for val in values.itervalues() if val.Value == value)
        setattr(cls,'__reverseLookup__',reverseLookup)
        setattr(cls,'__enumerables__',values)
        setattr(cls,'_Max',max([key for key in reverseLookup] or [0]))
        return super(EnumMeta,cls).__init__(name,bases,atts)

    def __iter__(cls):
        for enum in cls.__enumerables__.itervalues():
            yield enum
    def GetEnumByName(cls,name):
        return cls.__enumerables__.get(name,None)
    def GetEnumByValue(cls,value):
        return cls.__reverseLookup__.get(value,(None,))[0]

class Enum(object):
    __metaclass__=EnumMeta
    __enumerables__=None

class FlagEnum(Enum):
    @staticmethod
    def __nextitr__():
        yield 0
        for val in itertools.count():
            yield 2**val

def enum(name,*args):
    return EnumMeta(name,(Enum,),dict(__enumerables__=args))

Возьми или оставь, он сделал то, что мне нужно было сделать :)

Используйте это как:

class Air(FlagEnum):
    __enumerables__=('None','Oxygen','Nitrogen','Hydrogen')

class Mammals(Enum):
    __enumerables__=('Bat','Whale',('Dog','Puppy',1),'Cat')
Bool = enum('Bool','Yes',('No',0))

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

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.

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

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

Если вам нужен enum только как список имена, идентифицирующий разные Предметы, решение Марк Харрисон (см. Выше) будет отличным:

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

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

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

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

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

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

stype = Stationery.Pen

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

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

Больше информации:

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

с range вы можете опустить первый аргумент, если он 0

ToonAlfrink 13.06.2014 20:09

Еще одно фальшивое перечисление, которое подходит для некоторых целей, - my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2')))). Затем my_enum может использоваться для поиска, например, my_enum['Item0'] может быть индексом в последовательности. Вы можете заключить результат str.split в функцию, которая генерирует исключение, если есть какие-либо дубликаты.

Ana Nimbus 02.06.2018 06:51

Отлично! Для флагов вы можете Flag1, Flag2, Flag3 = [2**i for i in range(3)]

majkelx 30.09.2019 15:00

Это лучший ответ

Yuriy Pozniak 09.05.2020 05:53

Используйте следующее.

TYPE = {'EAN13':   u'EAN-13',
        'CODE39':  u'Code 39',
        'CODE128': u'Code 128',
        'i25':     u'Interleaved 2 of 5',}

>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']

Я использовал это для выбора модели Джанго, и он выглядит очень питоническим. На самом деле это не Enum, но он выполняет свою работу.

Ответ принят как подходящий

Перечисления были добавлены в Python 3.4, как описано в PEP 435. Это также было обратный перенос на 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 и 2.4 на pypi.

Для более продвинутых методов Enum попробуйте библиотека 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'

Если вы используете mypy, другой способ выражения «перечислений» - Буквальный.

Например

from typing import Literal #python >=3.8
from typing_extensions import Literal #python 2.7, 3.4-3.7


Animal = Literal['ant', 'bee', 'cat', 'dog']

def hello_animal(animal: Animal):
    print(f"hello {animal}")

hello_animal('rock') # error
hello_animal('bee') # passes

Я не мог понять, почему они передали kwargs (** named) в методе enum (* sequence, ** named)? Пожалуйста, объясните. Без kwargs тоже будет работать. Я проверил это.

Seenu S 10.03.2017 08:52

Было бы неплохо обновить функцию Python 2, чтобы она была совместима с функциональным API Python 3 Enum (имя, значения)

bscan 10.03.2017 18:52

Var kwargs (**named) в функции перечисления для более старых версий должен поддерживать пользовательские значения: enum("blue", "red", "green", black=0)

Éric Araujo 28.01.2020 01:20

Мне потребовались некоторые символические константы в 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 (), но пока это относится к категории ЯГНИ.

Это лучшее, что я видел: "Перечисления первого класса в 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)

Этот рецепт был использован в качестве основы для PEP, который был отклонен. python.org/dev/peps/pep-0354 Одно расширение, которое мне нравится: значения перечисления должны иметь переменную-член, которая позволяет вам получать внутреннее целочисленное значение. Невозможно по ошибке преобразовать перечисление в целое число, поэтому метод .__int__() должен вызывать исключение для перечисления; но должен быть способ получить выгоду. И должна быть возможность устанавливать определенные целочисленные значения во время определения класса, чтобы вы могли использовать перечисление для таких вещей, как константы в модуле stat.

steveha 22.03.2012 02:41

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

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

Вот его использование:

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

print(Animals.DOG)

Отлично. Это можно улучшить, переопределив __setattr__(self, name, value) и, возможно, __delattr__(self, name), так что если вы случайно напишете Animals.DOG = CAT, это не удастся молча.

Joonas Pulakka 11.01.2011 17:12

@shahjapan: Интересно, но относительно медленно: тест выполняется для каждого доступа, например Animals.DOG; Кроме того, значения констант являются строками, так что сравнение с этими константами происходит медленнее, чем если бы, скажем, в качестве значений допускались целые числа.

Eric O Lebigot 25.10.2011 16:48

@shahjapan: Я бы сказал, что это решение не так разборчиво, как, например, более короткие решения Александру или Марка. Но это интересное решение. :)

Eric O Lebigot 31.10.2011 13:39

Я попытался использовать функцию setattr() внутри метода __init__() вместо переопределения метода __getattr__(). Я предполагаю, что это должно работать одинаково: class Enum (object): def __init __ (self, enum_string_list): if type (enum_string_list) == list: for enum_string в enum_string_list: setattr (self, enum_string, enum_string) else: raise AttributeError

Harshith J.V. 15.11.2012 09:43

Я проголосовал за этот пост, скопировал и вставил код, не тестируя его. Теперь я вижу, что значение перечисления не целое число, а строка. Animals.DOG печатает «СОБАКА», а не 0.

user2233706 21.04.2014 08:49

@ AndréTerra: как проверить членство набора в блоке try-except?

bgusach 30.01.2015 14:57

@AndreTerra: А? Что бы вы добавили в свой пробный улов?

Clément 25.11.2016 23:52

Этот метод отлично подходит для строковых констант. Можно сделать "My pet is a "+Animals.DOG. Классу python3 Enum потребуется метод ул. для удаления имени класса и по-прежнему потребуется приведение str () ... если я чего-то не упускаю.

shao.lo 23.07.2017 22:07

Следуя реализации Java-подобного enum, предложенной Аароном Маенпаа, я сделал следующее. Идея заключалась в том, чтобы сделать его универсальным и доступным для анализа.

class Enum:
    #'''
    #Java like implementation for enums.
    #
    #Usage:
    #class Tool(Enum): name = 'Tool'
    #Tool.DRILL = Tool.register('drill')
    #Tool.HAMMER = Tool.register('hammer')
    #Tool.WRENCH = Tool.register('wrench')
    #'''

    name = 'Enum'    # Enum name
    _reg = dict([])   # Enum registered values

    @classmethod
    def register(cls, value):
        #'''
        #Registers a new value in this enum.
        #
        #@param value: New enum value.
        #
        #@return: New value wrapper instance.
        #'''
        inst = cls(value)
        cls._reg[value] = inst
        return inst

    @classmethod
    def parse(cls, value):
        #'''
        #Parses a value, returning the enum instance.
        #
        #@param value: Enum value.
        #
        #@return: Value corresp instance.        
        #'''
        return cls._reg.get(value)    

    def __init__(self, value):
        #'''
        #Constructor (only for internal use).
        #'''
        self.value = value

    def __str__(self):
        #'''
        #str() overload.
        #'''
        return self.value

    def __repr__(self):
        #'''
        #repr() overload.
        #'''
        return "<" + self.name + ": " + self.value + ">"

Я не могу понять, как этот репр может быть использован для воспроизведения объекта.

Tony Veijalainen 20.07.2010 20:00

@ Тони, этого не требуется, но это хорошо.

Jeremy 26.07.2011 08:34

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

Почему перечисления должны быть целыми числами? К сожалению, я не могу придумать какую-либо красивую конструкцию для создания этого без изменения языка Python, поэтому я буду использовать строки:

class Enumerator(object):
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        if self.name == other:
            return True
        return self is other

    def __ne__(self, other):
        if self.name != other:
            return False
        return self is other

    def __repr__(self):
        return 'Enumerator({0})'.format(self.name)

    def __str__(self):
        return self.name

class Enum(object):
    def __init__(self, *enumerators):
        for e in enumerators:
            setattr(self, e, Enumerator(e))
    def __getitem__(self, key):
        return getattr(self, key)

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

Пример:

class Cow(object):
    State = Enum(
        'standing',
        'walking',
        'eating',
        'mooing',
        'sleeping',
        'dead',
        'dying'
    )
    state = State.standing

In [1]: from enum import Enum

In [2]: c = Cow()

In [3]: c2 = Cow()

In [4]: c.state, c2.state
Out[4]: (Enumerator(standing), Enumerator(standing))

In [5]: c.state == c2.state
Out[5]: True

In [6]: c.State.mooing
Out[6]: Enumerator(mooing)

In [7]: c.State['mooing']
Out[7]: Enumerator(mooing)

In [8]: c.state = Cow.State.dead

In [9]: c.state == c2.state
Out[9]: False

In [10]: c.state == Cow.State.dead
Out[10]: True

In [11]: c.state == 'dead'
Out[11]: True

In [12]: c.state == Cow.State['dead']
Out[11]: True

Если у вас есть целочисленное поле в mysql db, вам часто нужны int enums

wim 11.02.2014 19:58

def enum( *names ):

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

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

Мне нравится перечисление Ява, вот как я делаю это в Python:

def enum(clsdef):
    class Enum(object):
        __slots__=tuple([var for var in clsdef.__dict__ if isinstance((getattr(clsdef, var)), tuple) and not var.startswith('__')])

        def __new__(cls, *args, **kwargs):
            if not '_the_instance' in cls.__dict__:
                cls._the_instance = object.__new__(cls, *args, **kwargs)
            return cls._the_instance

        def __init__(self):
            clsdef.values=lambda cls, e=Enum: e.values()
            clsdef.valueOf=lambda cls, n, e=self: e.valueOf(n)
            for ordinal, key in enumerate(self.__class__.__slots__):
                args=getattr(clsdef, key)
                instance=clsdef(*args)
                instance._name=key
                instance._ordinal=ordinal
                setattr(self, key, instance)

        @classmethod
        def values(cls):
            if not hasattr(cls, '_values'):
                cls._values=[getattr(cls, name) for name in cls.__slots__]
            return cls._values

        def valueOf(self, name):
            return getattr(self, name)

        def __repr__(self):
            return ''.join(['<class Enum (', clsdef.__name__, ') at ', str(hex(id(self))), '>'])

    return Enum()

Пример использования:

i=2
@enum
class Test(object):
    A=("a",1)
    B=("b",)
    C=("c",2)
    D=tuple()
    E=("e",3)

    while True:
        try:
            F, G, H, I, J, K, L, M, N, O=[tuple() for _ in range(i)]
            break;
        except ValueError:
            i+=1

    def __init__(self, name = "default", aparam=0):
        self.name=name
        self.avalue=aparam

Все переменные класса определяются как кортеж, как и конструктор. Пока вы не можете использовать именованные аргументы.

python3k, кстати, не знаю, работает ли он на 2.x

daegga 04.06.2010 20:42

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

Мне это нравится, но вы могли бы также заблокировать значения для эффективности с помощью кортежа? Я поигрался с этим и придумал версию, которая устанавливает self.values ​​из аргументов в в этом. Приятно иметь возможность объявить Animal = Enum('horse', 'dog', 'cat'). Я также ловлю ValueError в getattr в случае отсутствия элемента в self.values ​​- кажется, лучше вместо этого поднять AttributeError с предоставленной строкой имени. Я не смог заставить метакласс работать в Python 2.7 из-за моих ограниченных знаний в этой области, но мой собственный класс Enum отлично работает с прямыми методами экземпляра.

trojjer 28.11.2013 20:23

Я предпочитаю определять перечисления в Python так:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

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

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

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

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

Еще одна, очень простая реализация перечисления на Python с использованием namedtuple:

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

или, альтернативно,

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())




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

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


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

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

Как и описанный выше метод с подклассами set, это позволяет:

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

Но обладает большей гибкостью, поскольку может иметь разные ключи и значения. Это позволяет

MyEnum.FOO < MyEnum.BAR

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

Мне больше всего понравился этот подход, потому что он простой и понятный. Одно замечание для тех, кто хочет его использовать. Модуль collections будет перемещен в collections.abc в Python 3.10. Этот модуль, похоже, не включает namedtuple, поэтому вполне возможно, что namedtuple останется внутри модуля collections.

Luke Savefrogs 22.07.2020 12:15

Я понятия не имею, как должен работать именованный кортеж "enum" ... Это кортеж, а не перечисление, у него столько полей, сколько у "enum" значений, и все эти поля имеют строковые значения ... как его вообще использовать ??

Kuba hasn't forgotten Monica 23.02.2021 22:17

@ Kubahasn'tforgottenMonica Я не совсем понимаю, о чем вы спрашиваете. Что ты не понимаешь, как делать? В ответе я расскажу об основах.

agf 24.02.2021 03:52

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

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

Это действительно старый способ создания классов. Почему бы просто не использовать вместо него type(class_name, (object,), dict(...))?

terminus 01.01.2012 21:32

Мне приходилось нуждаться в классе 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

Мне очень нравится использование суперкласса с собственным метаклассом, чтобы упростить определение перечислений. Здесь не хватает метода __contains__. Я хотел бы иметь возможность проверить, является ли данная переменная частью перечисления - в основном потому, что мне нужны перечисления для допустимых значений параметра функции.

xorsyst 02.08.2012 13:37

На самом деле это немного урезанная версия той, которую я изначально создал (которую вы можете найти здесь: enum_strict.py) v, которая определяет метод __instancecheck__. Классы не являются коллекциями экземпляров, поэтому 1 in Fruit абсурден. Однако связанная версия поддерживает isinstance(1, Fruit), что было бы более правильным с точки зрения понятия классов и экземпляров.

SingleNegationElimination 02.08.2012 15:30

Но если забыть о классах и подумать о перечислениях, тогда имеет смысл думать о них как о коллекции. Например, у меня может быть список режимов открытия файлов (MODE.OPEN, MODE.WRITE и т. д.). Я хочу проверить параметры моей функции: если режим в РЕЖИМЕ: читает намного лучше, чем isintance (режим, режим)

xorsyst 06.08.2012 14:11

Я разместил что-то очень похожее, которое поддерживает больше, чем просто int, и задокументировано и протестировано на GitHub: github.com/hmeine/ named_constants

hans_meine 18.01.2017 11:47

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

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

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

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

NOTE: I created the following examples by trying to reproduce the behavior of pygtk's new style 'enums' (like 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'

эта идея "базового" типа хороша :)

MestreLion 23.05.2013 08:28

да, обратите внимание, что вы также можете сделать это с новым Python 3.4 Enum: python.org/dev/peps/pep-0435/#other-deved-enumerations

bj0 25.05.2013 12:23

Класс 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 2.4 (да, старый устаревший сервер) кортежи не индексируются. Я решил заменить на list.

Massimo 11.07.2017 15:37

Я попробовал это в записной книжке Jupyter и обнаружил, что это не будет работать как однострочное определение, но размещение определения getattr во второй (с отступом) строке будет приемлемым.

user5920660 20.07.2017 21:04

Это решение позволяет мне использовать ключевое слово in для поиска аккуратных членов. Пример использования: 'Claimed' in Enum(['Unclaimed', 'Claimed'])

Farzad Abdolhosseini 09.07.2019 02:34

Я использую метакласс для реализации перечисления (на мой взгляд, это константа). Вот код:

class ConstMeta(type):
    '''
    Metaclass for some class that store constants
    '''
    def __init__(cls, name, bases, dct):
        '''
        init class instance
        '''
        def static_attrs():
            '''
            @rtype: (static_attrs, static_val_set)
            @return: Static attributes in dict format and static value set
            '''
            import types
            attrs = {}
            val_set = set()
            #Maybe more
            filter_names = set(['__doc__', '__init__', '__metaclass__', '__module__', '__main__'])
            for key, value in dct.iteritems():
                if type(value) != types.FunctionType and key not in filter_names:
                    if len(value) != 2:
                        raise NotImplementedError('not support for values that is not 2 elements!')
                    #Check value[0] duplication.
                    if value[0] not in val_set:
                        val_set.add(value[0])
                    else:
                        raise KeyError("%s 's key: %s is duplicated!" % (dict([(key, value)]), value[0]))
                    attrs[key] = value
            return attrs, val_set

        attrs, val_set = static_attrs()
        #Set STATIC_ATTRS to class instance so that can reuse
        setattr(cls, 'STATIC_ATTRS', attrs)
        setattr(cls, 'static_val_set', val_set)
        super(ConstMeta, cls).__init__(name, bases, dct)

    def __getattribute__(cls, name):
        '''
        Rewrite the special function so as to get correct attribute value
        '''
        static_attrs = object.__getattribute__(cls, 'STATIC_ATTRS')
        if name in static_attrs:
            return static_attrs[name][0]
        return object.__getattribute__(cls, name)

    def static_values(cls):
        '''
        Put values in static attribute into a list, use the function to validate value.
        @return: Set of values
        '''
        return cls.static_val_set

    def __getitem__(cls, key):
        '''
        Rewrite to make syntax SomeConstClass[key] works, and return desc string of related static value.
        @return: Desc string of related static value
        '''
        for k, v in cls.STATIC_ATTRS.iteritems():
            if v[0] == key:
                return v[1]
        raise KeyError('Key: %s does not exists in %s !' % (str(key), repr(cls)))


class Const(object):
    '''
    Base class for constant class.

    @usage:

    Definition: (must inherit from Const class!
        >>> class SomeConst(Const):
        >>>   STATUS_NAME_1 = (1, 'desc for the status1')
        >>>   STATUS_NAME_2 = (2, 'desc for the status2')

    Invoke(base upper SomeConst class):
    1) SomeConst.STATUS_NAME_1 returns 1
    2) SomeConst[1] returns 'desc for the status1'
    3) SomeConst.STATIC_ATTRS returns {'STATUS_NAME_1': (1, 'desc for the status1'), 'STATUS_NAME_2': (2, 'desc for the status2')}
    4) SomeConst.static_values() returns set([1, 2])

    Attention:
    SomeCosnt's value 1, 2 can not be duplicated!
    If WrongConst is like this, it will raise KeyError:
    class WrongConst(Const):
        STATUS_NAME_1 = (1, 'desc for the status1')
        STATUS_NAME_2 = (1, 'desc for the status2')
    '''
    __metaclass__ = ConstMeta
##################################################################
#Const Base Class ends
##################################################################


def main():
    class STATUS(Const):
        ERROR = (-3, '??')
        OK = (0, '??')

    print STATUS.ERROR
    print STATUS.static_values()
    print STATUS.STATIC_ATTRS

    #Usage sample:
    user_input = 1
    #Validate input:
    print user_input in STATUS.static_values()
    #Template render like:
    print '<select>'
    for key, value in STATUS.STATIC_ATTRS.items():
        print '<option value = "%s">%s</option>' % (value[0], value[1])
    print '</select>'


if __name__ == '__main__':
    main()

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

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

Мне нравится использовать списки или наборы в качестве перечислений. Например:

>>> packet_types = ['INIT', 'FINI', 'RECV', 'SEND']
>>> packet_types.index('INIT')
0
>>> packet_types.index('FINI')
1
>>>

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

def enum(names):
    "Create a simple enumeration having similarities to C."
    return type('enum', (), dict(map(reversed, enumerate(
        names.replace(',', ' ').split())), __slots__=()))()

Использовать его так же просто, как вызвать функцию со строкой, имеющей имена, на которые вы хотите ссылаться.

grade = enum('A B C D F')
state = enum('awake, sleeping, dead')

Значения представляют собой просто целые числа, поэтому при желании вы можете воспользоваться этим (как в языке C).

>>> grade.A
0
>>> grade.B
1
>>> grade.F == 4
True
>>> state.dead == 2
True

Python 2.7 и find_name ()

Вот простая для чтения реализация выбранной идеи с некоторыми вспомогательными методами, которые, возможно, более питоничны и чище в использовании, чем "reverse_mapping". Требуется Python> = 2.7.

Чтобы ответить на некоторые комментарии ниже, перечисления весьма полезны для предотвращения орфографических ошибок в коде, например для конечных автоматов, классификаторов ошибок и т. д.

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

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

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

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

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

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

По состоянию на апрель 2013 года, похоже, существует общий консенсус в отношении того, что что нибудь следует добавить в стандартную библиотеку в 3.4 - при условии, что люди могут прийти к соглашению о том, каким должно быть это «нечто». Это самая сложная часть. Посмотрите потоки, запускающие здесь и здесь, и полдюжины других потоков в первые месяцы 2013 года.

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

10 мая 2013 г. Гвидо согласился принять 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

Для получения дополнительной информации обратитесь к предложение. Официальная документация, вероятно, появится в ближайшее время.

Новый стандарт в 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

Вот подход с некоторыми характеристиками, которые я считаю ценными:

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

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

Основано на 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

Не видел этого в списке ответов, вот тот, который я придумал. Он позволяет использовать ключевое слово in и метод len ():

class EnumTypeError(TypeError):
    pass

class Enum(object):
    """
    Minics enum type from different languages
    Usage:
    Letters = Enum(list('abc'))
    a = Letters.a
    print(a in Letters) # True
    print(54 in Letters) # False
    """
    def __init__(self, enums):
        if isinstance(enums, dict):
            self.__dict__.update(enums)
        elif isinstance(enums, list) or isinstance(enums, tuple):
            self.__dict__.update(**dict((v,k) for k,v in enumerate(enums)))
        else:
            raise EnumTypeError

    def __contains__(self, key):
        return key in self.__dict__.values()

    def __len__(self):
        return len(self.__dict__.values())


if __name__ == '__main__':
    print('Using a dictionary to create Enum:')
    Letters = Enum(dict((v,k) for k,v in enumerate(list('abcde'))))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    print('\nUsing a list to create Enum:')
    Letters = Enum(list('abcde'))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    try:
        # make sure we raise an exception if we pass an invalid arg
        Failure = Enum('This is a Failure')
        print('Failure')
    except EnumTypeError:
        print('Success!')

Выход:

Using a dictionary to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5

Using a list to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5
Success!

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

Enumerations are created using the class syntax, which makes them easy to read and write. An alternative creation method is described in Functional API. To define an enumeration, subclass Enum as follows:

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

Также теперь поддерживается обратное портирование. Это путь.

srock 10.06.2014 23:10

Вот хороший рецепт Python, который я нашел здесь: http://code.activestate.com/recipes/577024-yet-another-enum-for-python/

def enum(typename, field_names):
    "Create a new enumeration type"

    if isinstance(field_names, str):
        field_names = field_names.replace(',', ' ').split()
    d = dict((reversed(nv) for nv in enumerate(field_names)), __slots__ = ())
    return type(typename, (object,), d)()

Пример использования:

STATE = enum('STATE', 'GET_QUIZ, GET_VERSE, TEACH')

Более подробную информацию можно найти на странице рецептов.

Будь проще:

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

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

Ой!

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