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

Производные класса 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.StrEnum.

Добавлен в Python 3.11.

Класс enum.StrEnum является подклассом str. Элементы enum.StrEnum можно сравнивать со строками. По расширению, строковые перечисления разных типов также можно сравнивать друг с другом.

Класс enum.IntFlag.

Добавлен в Python 3.6.

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

Примечание. Любая операция над элементом enum.IntFlag, кроме побитовых, потеряет членство в enum.IntFlag.

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

Изменено в Pytrhon 3.11.

Пример класса 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: 0>
>>> Perm(7)
# <Perm.RWX: 7>

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

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

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

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

>>> Perm.X | 4
# <Perm.R|X: 5>

>>> Perm.X | 8
# 9

Примечание. Оператор отрицания ~ всегда возвращает элемент enum.IntFlag с положительным значением:

>>> (~Perm.X).value == (Perm.R|Perm.W).value == 6
#True

Члены enum.IntFlag также могут быть повторены (Новое в Python 3.11):

list(RW)
[<Perm.R: 4>, <Perm.W: 2>]

Класс enum.Flag.

Новое в Python 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>
>>> 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.Flag также могут быть повторены (Новое в Python 3.11):

>>> purple = Color.RED | Color.BLUE
>>> list(purple)
# [<Color.RED: 1>, <Color.BLUE: 2>]

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


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

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

class IntEnum(int, Enum):
    pass

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

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

  1. При создании подкласса Enum смешанные типы должны появляться перед самим Enum в последовательности оснований, как в примере с IntEnum выше.
  2. Смешанные типы должны иметь подклассы. Например, bool и range не являются подклассами и вызовут ошибку во время создания Enum, если они используются в качестве типа микширования.
  3. Хотя Enum может иметь члены любого типа, однако после добавления дополнительного типа все члены должны иметь значения этого типа, например int. Это ограничение не применяется к миксам, которые только добавляют методы и не указывают другой тип.
  4. Когда добавляется другой тип данных, то атрибут value не совпадает с самим членом перечисления, хотя он эквивалентен и будет в сравнениях равным.
  5. форматирование в стиле %: %s и %r вызывают __str__() и __repr__() класса Enum соответственно. Другие коды (например, %i или %h для IntEnum) рассматривают член перечисления как его смешанный тип.
  6. Форматированные строковые литералы (f-строки), str.format() и format() будут использовать метод перечисления __str__().

Примечание. Поскольку IntEnum, IntFlag и StrEnum предназначены для замены существующих констант, их метод __str__() был сброшен на метод их типов данных __str__().