enum — Поддержка перечислений

Добавлено в версии 3.4.

Исходный код: Lib/enum.py


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

Содержание модуля

Этот модуль определяет четыре класса перечисления, которые могут быть используемый, чтобы определить уникальные наборы имен и значения: Enum, IntEnum, Flag и IntFlag. Он также определяет один декоратор, unique() и один помощник, auto.

class enum.Enum

Базовый класс для создания перечисляемых констант. Альтернативный синтаксис построения см. в разделе Функциональный API.

class enum.IntEnum

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

class enum.IntFlag

Базовый класс для создания перечисляемых констант, которые могут быть объединены с помощью побитовых операторов без потери поля IntFlag. IntFlag полея также являются подклассы int.

class enum.Flag

Базовый класс для создания перечисляемых констант, которые могут быть объединены с помощью побитовых операций без потери поля Flag.

enum.unique()

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

class enum.auto

сущности заменяются соответствующим значение для участников Enum. Начальное значение начинается с 1.

Добавлено в версии 3.6: Flag, IntFlag, auto

Создание Enum

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

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...

Примечание

Перечислите значений поля

Членом значения может быть что угодно: int, str и т.д.. Если точное значение неважно, вы можете использовать auto сущности и для вас будет выбран подходящий значение. Если вы смешиваете auto с другими значения, необходимо соблюдать осторожность.

Примечание

Номенклатура

  • Класс Color является enumeration (или enum)
  • Атрибуты Color.RED, Color.GREEN и т.д. являются enumeration members (или enum members) и функционально постоянными.
  • поля перечисления имеют names и values (название Color.RED - RED, значение Color.BLUE - 3 и т. д.)

Примечание

Даже при том, что мы используем синтаксис class, чтобы создать Enums, Enums не нормальные классы Python. Дополнительные сведения см. в разделе Чем Enums отличаются?.

Члены перечисления имеют читаемые человеком представления строка:

>>> print(Color.RED)
Color.RED

… в то время как их repr имеет больше информации:

>>> print(repr(Color.RED))
<Color.RED: 1>

type поля перечисления является перечислением, которому он принадлежит:

>>> type(Color.RED)
<enum 'Color'>
>>> isinstance(Color.GREEN, Color)
True
>>>

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

>>> print(Color.RED.name)
RED

Перечисления поддерживают итерацию в порядке определения:

>>> class Shake(Enum):
...     VANILLA = 7
...     CHOCOLATE = 4
...     COOKIES = 9
...     MINT = 3
...
>>> for shake in Shake:
...     print(shake)
...
Shake.VANILLA
Shake.CHOCOLATE
Shake.COOKIES
Shake.MINT

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

>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True

Программный доступ к полям переписей и их атрибуты

Иногда полезно обращаться к полям в перечислениях программно (т.е. ситуации, когда Color.RED не будет делать, потому что точный цвет не известен во время написания программы). Enum разрешает такой доступ:

>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>

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

>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>

Если у вас есть поле перечисления и вам нужен его name или value:

>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1

Дублирование элементов enum и значения

Наличие двух полей перечисления с одинаковым именем недопустимо:

>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'SQUARE'

Однако два элемента перечисления имеют одинаковую значение. Учитывая два поля A и B с одним и тем же значение (и A определено первым), B является алиас к A. By-значение поиск значение A и B будет возвращает A. Поиск по имени для B также будет возвращает A:

>>> class Shape(Enum):
...     SQUARE = 2
...     DIAMOND = 1
...     CIRCLE = 3
...     ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
<Shape.SQUARE: 2>
>>> Shape.ALIAS_FOR_SQUARE
<Shape.SQUARE: 2>
>>> Shape(2)
<Shape.SQUARE: 2>

Примечание

Пытаясь создать поля с тем же именем как уже определенный атрибут (другой поле, метод, и т.д.) или пытаясь создать атрибут с тем же именем, поскольку полю не разрешают.

Обеспечение уникального перечисления значения

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

@enum.unique

Декоратор class специально для перечислений. Он выполняет поиск __members__ перечисления, собирающего любые найденные псевдонимы; если кто-либо найден, ValueError поднят с деталями:

>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
...     ONE = 1
...     TWO = 2
...     THREE = 3
...     FOUR = 3
...
Traceback (most recent call last):
...
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

Используя автоматический значения

Если точная значение неважна, можно использовать auto:

>>> from enum import Enum, auto
>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

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

>>> class AutoName(Enum):
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

Примечание

Целью методов _generate_next_value_() по умолчанию является предоставление следующего int в последовательности с последним предоставленным int, но способ его выполнения является детализацией реализации и может измениться.

Итерация

Итерация по элементам перечисления не предоставляет псевдонимов:

>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]

Специальный атрибут __members__ представляет собой упорядоченное отображение имен полям только для чтения. Он включает все имена, определенные в перечислении, включая псевдонимы:

>>> for name, member in Shape.__members__.items():
...     name, member
...
('SQUARE', <Shape.SQUARE: 2>)
('DIAMOND', <Shape.DIAMOND: 1>)
('CIRCLE', <Shape.CIRCLE: 3>)
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)

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

>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']

Сравнения

Члены перечисления сравниваются по идентификатору:

>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True

Упорядоченные сравнения значения перечисления не поддерживаются. Члены Enum не целые числа (но см. IntEnum ниже):

>>> Color.RED < Color.BLUE
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'

При этом определяются сравнения равенства:

>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == Color.BLUE
True

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

>>> Color.BLUE == 2
False

Допустимые поля и атрибуты перечислений

В приведенных выше примерах для перечисления значения используются целые числа. Использование целых чисел является коротким и удобным (и предоставляется по умолчанию Функциональный API), но не строго соблюдается. В подавляющем большинстве случаев использования не важно, какова фактическая значение перечисления. Но если важный значение is, у перечислений может быть произвольный значения.

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

>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self is the member here
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls here is the enumeration
...         return cls.HAPPY
...

Тогда:

>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'

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

Примечание: если ваше перечисление определит __new__() и/или __init__() тогда независимо от того, что значение(я) были даны enum полю, будет передан в те методы. Пример см. в разделе Планета.

Ограниченная подклассификация Enum

Новый класс Enum должен иметь один базовый класс Enum, до одного конкретного типа данных и столько классов object-based mixin, сколько необходимо. Порядок этих базовых классов равен:

class EnumName([mix-in, ...,] [data-type,] base-enum):
    pass

Кроме того, подкласс перечисления разрешен только в том случае, если перечисление не определяет полей. Так что это запрещено:

>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: Cannot extend enumerations

Но это разрешено:

>>> class Foo(Enum):
...     def some_behavior(self):
...         pass
...
>>> class Bar(Foo):
...     HAPPY = 1
...     SAD = 2
...

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

Пиклинг

Перечисления могут быть маринованы и отменены:

>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True

Обычные ограничения для пиклинг применяются: picklable enums должен быть определен в высшем уровне модуля, так как несоление требует, чтобы они были разрешены к ввозу от того модуля.

Примечание

С помощью протокола pickle версии 4 можно легко pickle элементы, вложенные в другие классы.

Можно изменить способ подбора/отмены выбора элементов Enum путем определения __reduce_ex__() в классе перечисления.

Функциональный API

Класс Enum является вызываемым, предоставляя следующий функциональный API:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> Animal.ANT.value
1
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

Семантика этого API напоминает namedtuple. Первым аргументом вызова метода Enum является имя перечисления.

Второй аргумент - это source имен полей перечисления. Это может быть отделенный от пробела строка имен, последовательность имен, последовательность 2 кортежей с key/значение парами или отображение (например, словарь) имен к значения. Последние два варианта позволяют назначать произвольные значения перечислениям; другие автоматически назначают увеличивающиеся целые числа, начиная с 1 (используйте параметр start, чтобы указать другой начальный значение). Возвращается новый класс, производный от Enum. Другими словами, вышеуказанное присвоение Animal эквивалентно:

>>> class Animal(Enum):
...     ANT = 1
...     BEE = 2
...     CAT = 3
...     DOG = 4
...

Причина невыполнения обязательств к 1 как стартовое число и не 0 состоит в том, что 0 - False в булевом смысле, но enum поля, которых все оценивают к True.

Черпающие перечисления, созданные с помощью функционального API, могут быть сложными, так как детали реализации стека кадров используемый пытаться выяснить, в каком модуле создается перечисление (например, если вы используете служебную функцию в отдельном модуле, а также можете не работать с IronPython или Jython). Решение заключается в явном указании имени модуля следующим образом:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)

Предупреждение

Если module не предоставляется и Enum не может определить, что это такое, новые поля Enum не могут быть отменены; чтобы сохранить ошибки ближе к источнику, пиклинг будет отключен.

Новый протокол 4 pickle также, при некоторых обстоятельствах, полагается на __qualname__, устанавливаемый в местоположение, где pickle будет в состоянии найти класс. Например, если класс был доступен в классе SomeData в глобальном область видимости:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')

Полный сигнатура есть:

Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
value:

что новый класс Enum запишет в качестве своего названия.

names:

участники перечисления. Это может быть пробел или разделенное запятыми строка (значения начинается с 1, если не указано иное):

'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'

или итератор имен:

['RED', 'GREEN', 'BLUE']

или итератор пар (имя, значение):

[('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]

или отображение:

{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
module:

имя модуля, где можно найти новый класс Enum.

qualname:

где в модуле можно найти новый класс Enum.

type:

тип, чтобы смешать с новым классом Enum.

start:

номер для начала отсчета, где только имена в.

Изменено в версии 3.5: добавлен Был добавлен параметр start.

Полученные перечисления

IntEnum

Первый вариант Enum, который предоставляется, также является подкласс int. Члены IntEnum можно сравнить с целыми числами; по расширению целочисленные перечисления различных типов также могут сравниваться друг с другом:

>>> from enum import IntEnum
>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Request(IntEnum):
...     POST = 1
...     GET = 2
...
>>> Shape == 1
False
>>> Shape.CIRCLE == 1
True
>>> Shape.CIRCLE == Request.POST
True

Однако они все еще не могут сравниться со стандартными перечислениями Enum:

>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...
>>> Shape.CIRCLE == Color.RED
False

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

>>> int(Shape.CIRCLE)
1
>>> ['a', 'b', 'c'][Shape.CIRCLE]
'b'
>>> [i for i in range(Shape.SQUARE)]
[0, 1]

IntFlag

Следующий вариант предоставленных Enum, IntFlag, также основан на int. Так как различие - поля IntFlag, может быть объединен, используя логические операторы (&, |, ^, ~), и результат - все еще поле IntFlag. Однако, поскольку имя подразумевает, поля IntFlag также подкласс int и может быть используемый везде, где int - используемый. Любая операция на поле IntFlag помимо битовых операций потеряет поля IntFlag.

Добавлено в версии 3.6.

Типовой класс IntFlag:

>>> from enum import IntFlag
>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...
>>> Perm.R | Perm.W
<Perm.R|W: 6>
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
>>> Perm.R in RW
True

Также можно назвать комбинации:

>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...     RWX = 7
>>> Perm.RWX
<Perm.RWX: 7>
>>> ~Perm.RWX
<Perm.-8: -8>

Другое важное отличие между IntFlag и Enum состоит в том, что если флаги не установлены (значение равен 0), его логическая оценка является False:

>>> Perm.R & Perm.X
<Perm.0: 0>
>>> bool(Perm.R & Perm.X)
False

Поскольку поля IntFlag - также подклассы int, они могут быть объединены с ними:

>>> Perm.X | 8
<Perm.8|X: 9>

Флаг

Последний вариант - Flag. Как и IntFlag, элементы Flag можно объединять с помощью побитовых операторов (& ,|, ^, ~). В отличие от IntFlag, они не могут быть объединены с, ни сравнены с, любое другое перечисление Flag, ни int. В то время как можно указать значения напрямую, рекомендуется использовать auto в качестве значение и дать Flag выбрать соответствующий значение.

Добавлено в версии 3.6.

Как и IntFlag, если комбинация элементов Flag не приводит к установке флагов, логическая оценка является False:

>>> from enum import Flag, auto
>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color.0: 0>
>>> bool(Color.RED & Color.GREEN)
False

Отдельные флаги должны иметь значения, которые являются степенями двух (1, 2, 4, 8,…), в то время как комбинации флагов не будут:

>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...     WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
<Color.WHITE: 7>

Давая имя к «никаким флагам, установленным», условие не изменяет свой булев значение:

>>> class Color(Flag):
...     BLACK = 0
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.BLACK
<Color.BLACK: 0>
>>> bool(Color.BLACK)
False

Примечание

Для большинства новых код настоятельно рекомендуется Enum и Flag, так как IntEnum и IntFlag ломают некоторые семантические обещания перечисления (по сопоставимости с целыми числами, и, таким образом, по переходности к другим несвязанным перечислениям). IntEnum и IntFlag должны быть используемый только в случаях, где Enum и Flag не сделают; например, когда целочисленные константы заменяются перечислениями, или для совместимости с другими системами.

Другие

Хотя IntEnum является частью модуля enum, его было бы очень просто реализовать независимо:

class IntEnum(int, Enum):
    pass

Это демонстрирует, как можно определить аналогичные производные перечисления; например, StrEnum, который смешивается в str вместо int.

Некоторые правила:

  1. Подклассифицируя Enum, соединение - в типах должно появиться перед самим Enum в последовательности оснований, как в примере IntEnum выше.
  2. Хотя Enum может иметь элементы любого типа, после смешивания с дополнительным типом все элементы должны иметь значения этого типа, например, int выше. Это ограничение не применяется к смешанным элементам, которые только добавляют методы и не указывают другой тип данных, например int или str.
  3. Когда другой тип данных смешан в, value атрибут - не то же самое как enum поле сам, хотя это эквивалентно и выдержит сравнение равный.
  4. % - форматирование стиля: %s и %r называют класс __str__() и __repr__() Enum соответственно; другие коды (например, %i или %h для IntEnum) рассматривают поле перечисления как его смешанный тип.
  5. Отформатированные строковые литералы, str.format() и format() будут использовать __format__() смешанного типа. Если требуется str() или repr() класса Enum, используйте формат !s или !r коды.

Когда использовать __new__() против __init__()

__new__() должны быть используемый всякий раз, когда вы хотите настроить фактическую значение элемента Enum. Любые другие модификации могут войти или в __new__() или в __init__(), при этом __init__() предпочтен.

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

>>> class Coordinate(bytes, Enum):
...     """
...     Coordinate with binary codes that can be indexed by the int code.
...     """
...     def __new__(cls, value, label, unit):
...         obj = bytes.__new__(cls, [value])
...         obj._value_ = value
...         obj.label = label
...         obj.unit = unit
...         return obj
...     PX = (0, 'P.X', 'km')
...     PY = (1, 'P.Y', 'km')
...     VX = (2, 'V.X', 'km/s')
...     VY = (3, 'V.Y', 'km/s')
...

>>> print(Coordinate['PY'])
Coordinate.PY

>>> print(Coordinate(3))
Coordinate.VY

Интересные примеры

Хотя ожидается, что Enum, IntEnum, IntFlag и Flag охватят большинство случаев использования, они не могут охватить их все. Вот рецепты для некоторых различных типов перечислений, которые могут быть используемый непосредственно, или в качестве примеров для создания своего собственного.

Исключение значения

Во многих случаях использования не важно, какова фактическая значение перечисления. Существует несколько способов определения этого типа простого перечисления:

  • использование сущности auto для значение
  • использования сущности object в качестве значение
  • использовать описательный строка в качестве значение
  • использовать кортеж в качестве значение и пользовательский __new__() для замены кортежа на int значение

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

Какой бы метод вы ни выбрали, вы должны предоставить repr(), который также скрывает (неважный) значение:

>>> class NoValue(Enum):
...     def __repr__(self):
...         return '<%s.%s>' % (self.__class__.__name__, self.name)
...

Использование auto

Использование auto будет выглядеть как:

>>> class Color(NoValue):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN>

Использование object

Использование object будет выглядеть как:

>>> class Color(NoValue):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN>

Использование описательной строки

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

>>> class Color(NoValue):
...     RED = 'stop'
...     GREEN = 'go'
...     BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
'go'

Использование пользовательских __new__()

Используя автоматическую нумерацию __new__() был бы похож:

>>> class AutoNumber(NoValue):
...     def __new__(cls):
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...
>>> class Color(AutoNumber):
...     RED = ()
...     GREEN = ()
...     BLUE = ()
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
2

Примечание

Метод __new__(), если определено, является используемый во время создания полей Enum; затем он заменяется __new__() Enum, который используемый после создания класса для поиска существующих полей.

OrderedEnum

Заказанное перечисление, которое не основано на IntEnum и так поддерживает нормальные инварианты Enum (такой как не являющийся сопоставимым с другими перечислениями):

>>> class OrderedEnum(Enum):
...     def __ge__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value >= other.value
...         return NotImplemented
...     def __gt__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value > other.value
...         return NotImplemented
...     def __le__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value <= other.value
...         return NotImplemented
...     def __lt__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value < other.value
...         return NotImplemented
...
>>> class Grade(OrderedEnum):
...     A = 5
...     B = 4
...     C = 3
...     D = 2
...     F = 1
...
>>> Grade.C < Grade.A
True

Реплицируемый Enum

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

>>> class DuplicateFreeEnum(Enum):
...     def __init__(self, *args):
...         cls = self.__class__
...         if any(self.value == e.value for e in cls):
...             a = self.name
...             e = cls(self.value).name
...             raise ValueError(
...                 "aliases not allowed in DuplicateFreeEnum:  %r --> %r"
...                 % (a, e))
...
>>> class Color(DuplicateFreeEnum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...     GRENE = 2
...
Traceback (most recent call last):
...
ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'

Примечание

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

Планета

Если определен __new__() или __init__(), то значение элемента перечисления будет передан этим методам:

>>> class Planet(Enum):
...     MERCURY = (3.303e+23, 2.4397e6)
...     VENUS   = (4.869e+24, 6.0518e6)
...     EARTH   = (5.976e+24, 6.37814e6)
...     MARS    = (6.421e+23, 3.3972e6)
...     JUPITER = (1.9e+27,   7.1492e7)
...     SATURN  = (5.688e+26, 6.0268e7)
...     URANUS  = (8.686e+25, 2.5559e7)
...     NEPTUNE = (1.024e+26, 2.4746e7)
...     def __init__(self, mass, radius):
...         self.mass = mass       # in kilograms
...         self.radius = radius   # in meters
...     @property
...     def surface_gravity(self):
...         # universal gravitational constant  (m3 kg-1 s-2)
...         G = 6.67300E-11
...         return G * self.mass / (self.radius * self.radius)
...
>>> Planet.EARTH.value
(5.976e+24, 6378140.0)
>>> Planet.EARTH.surface_gravity
9.802652743337129

TimePeriod

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

>>> from datetime import timedelta
>>> class Period(timedelta, Enum):
...     "different lengths of time"
...     _ignore_ = 'Period i'
...     Period = vars()
...     for i in range(367):
...         Period['day_%d' % i] = i
...
>>> list(Period)[:2]
[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
>>> list(Period)[-2:]
[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]

Чем Enums отличаются?

Перечисления имеют пользовательский метакласс, который затрагивает многие аспекты как производных классов Enum, так и их сущности (полей).

Классы Enum

EnumMeta метакласс отвечает за предоставление методов __contains__(), __dir__(), __iter__() и других методов, которые позволяют выполнять действия с классом Enum, отказавшим в обычном классе, например list(Color) или some_enum_var in Color. EnumMeta отвечает за обеспечение правильности различных других методов для конечного класса Enum (таких как __new__(), __getnewargs__(), __str__() и __repr__()).

Члены перечисления (иначе сущности)

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

Нюансы

Поддержка __dunder__ имен

__members__ является упорядоченным отображением элементов member_name:member только для чтения. Он доступен только в классе.

__new__(), если указано, должны создавать и возвращает элементы перечисления; это также очень хорошая идея, чтобы установить _value_ поля соответствующим образом. После создания всех элементов он больше не будет используемый.

Поддержка _sunder_ имен

  • _name_ – название поля
  • _value_ – значение поля; может быть установлен/изменен в __new__
  • _missing_ - функция подстановки используемый, когда значение не найден; может быть переопределен
  • _ignore_ - список имен, либо как list(), либо как str(), которые не будут преобразованы в поля и будут удалены из окончательного класса
  • _order_ - используемый в Python 2/3 код для обеспечения последовательности полей (класс атрибут, удален при создании класса)
  • _generate_next_value_ - используемый Функциональный API и auto для получения соответствующего значение для элемента перечисления; может быть переопределен

Добавлено в версии 3.6: _missing_, _order_, _generate_next_value_

Добавлено в версии 3.7: _ignore_

Чтобы сохранить синхронизацию Python 2/ Python 3 код, можно предоставить _order_ атрибут. Он будет проверен по фактическому порядку перечисления и вызовет ошибку, если они не совпадают:

>>> class Color(Enum):
...     _order_ = 'RED GREEN BLUE'
...     RED = 1
...     BLUE = 3
...     GREEN = 2
...
Traceback (most recent call last):
...
TypeError: member order does not match _order_

Примечание

В Python 2 код _order_ атрибут необходим, поскольку заказ определения потерян, прежде чем он сможет быть зарегистрирован.

Enum тип поля

Enum поля являются сущности их класса Enum и обычно доступны как EnumClass.member. При определенных обстоятельствах к ним можно также получить доступ как EnumClass.member.member, но вы никогда не должны делать этого, поскольку тот поиск может потерпеть неудачу или, хуже, возвращает something помимо поля Enum, которого вы ищете (это - другое серьезное основание использовать все-заглавные имена полей):

>>> class FieldTypes(Enum):
...     name = 0
...     value = 1
...     size = 2
...
>>> FieldTypes.value.size
<FieldTypes.size: 2>
>>> FieldTypes.size.value
2

Изменено в версии 3.5.

Булев значение классов Enum и полей

Enum поля, смешанные с типами не Enum (например, int, str и т.д.), оцениваются в соответствии с правилами смешанного типа; в противном случае все поля вычисляются как True. Чтобы собственная логическая оценка Enum зависела от значение участника, добавьте в класс следующее:

def __bool__(self):
    return bool(self.value)

Enum классы всегда вычисляются как True.

Enum классы с методами

Если вы даете ваши Enum подкласс дополнительные методы, как класс Планета выше, эти методы будут отображаться в dir() поля, но не класса:

>>> dir(Planet)
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']

Объединение полей Flag

Если комбинация элементов Flag не называется, repr() будет включать все именованные флаги и все именованные комбинации флагов, которые находятся в значение:

>>> class Color(Flag):
...     RED = auto()
...     GREEN = auto()
...     BLUE = auto()
...     MAGENTA = RED | BLUE
...     YELLOW = RED | GREEN
...     CYAN = GREEN | BLUE
...
>>> Color(3)  # именованная комбинация
<Color.YELLOW: 3>
>>> Color(7)      # неназванная комбинация
<Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>