Сообщить об ошибке.

Производные класса enum.Enum в Python.

Производные перечислений enum.Enum.

Производные класса enum.Enum включают в себя:

Класс enum.IntEnum.

Первый предоставленный вариант Enum также является подклассом int. Члены класса enum.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.Enum:

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

Значения enum.IntEnum ведут себя как целые числа и в других отношениях:

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

Класс enum.IntFlag.

Новое в версии 3.6.

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

Но, как следует из названия, члены enum.IntFlag также являются подклассом int и могут использоваться везде, где используется int. Любая операция с членом IntFlag, кроме побитовых, теряет членство в классе IntFlag.

Пример класса enum.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

Также можно именовать члены IntFlag комбинациями из других членов этого класса:

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

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

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

Поскольку члены enum.IntFlag также являются подклассами int, их можно комбинировать с ними:

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

Класс enum.Flag.

Новое в версии 3.6.

Последняя вариация - это enum.Flag. Как и enum.IntFlag, члены enum.Flag можно комбинировать с помощью побитовых операторов (&, |, ^, ~).

В отличие от enum.IntFlag, они не могут быть объединены или сравнены с любым другим перечислением enum.Flag или типом int. Хотя можно указать значения напрямую, рекомендуется использовать класс enum.auto в качестве значения и позволить классу Flag выбрать соответствующее значение.

Как и в случае с 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>

Присвоение имени условию "no flags set" не изменяет его логическое значение:

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

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


Пользовательские производные на основе класса enum.Enum.

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

class IntEnum(int, Enum):
    pass

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

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

  • При создании подкласса Enum смешанные типы должны появляться перед самим Enum в последовательности оснований, как в примере с IntEnum выше.
  • Хотя Enum может иметь члены любого типа, однако после добавления дополнительного типа все члены должны иметь значения этого типа, например int. Это ограничение не применяется к миксам, которые только добавляют методы и не указывают другой тип.
  • Когда добавляется другой тип данных, то атрибут value не совпадает с самим членом перечисления, хотя он эквивалентен и будет в сравнениях равным.
  • % - стилевое форматирование: %s и %r вызывают __str__() и __repr__() класса Enum соответственно. Другие коды (например, %i или %h для IntEnum) рассматривают член перечисления как его смешанный тип.
  • Форматированные строковые литералы, str.format() и format() будут использовать __format__() смешанного типа, если __str__() или format()не переопределены в подклассе, и в этом случае будут использоваться переопределенные методы или методыEnum. Используйте коды формата!Sи!R, чтобы принудительно использовать методыstr()иrepr()классаEnum`.