> **Источник:** https://python-all.ru/3.6/library/enum.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 8.13. [`enum`](https://python-all.ru/3.6/library/enum.html#module-enum) – Поддержка перечислений

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

**Исходный код:** [Lib/enum.py](https://python-all.ru/src/3.6/Lib/enum.py)

---

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

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

Этот модуль определяет четыре класса перечислений, которые можно использовать для определения уникальных наборов имён и значений: [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum), [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum), [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag) и [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag). Также он определяет один декоратор [`unique()`](https://python-all.ru/3.6/library/enum.html#enum.unique) и один вспомогательный класс [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto).

#### `class enum.Enum`

Базовый класс для создания перечислимых констант. См. раздел [Функциональный API](https://python-all.ru/3.6/library/enum.html#functional-api) для альтернативного синтаксиса создания.

#### `class enum.IntEnum`

Базовый класс для создания перечислимых констант, которые также являются подклассами [`int`](https://python-all.ru/3.6/library/functions.html#int).

#### `class enum.IntFlag`

Базовый класс для создания перечислимых констант, которые можно комбинировать с помощью побитовых операторов, не теряя принадлежности к [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag). Члены [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) также являются подклассами [`int`](https://python-all.ru/3.6/library/functions.html#int).

#### `class enum.Flag`

Базовый класс для создания перечисляемых констант, которые можно комбинировать с помощью побитовых операций без потери их принадлежности к [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag).

#### `enum.unique()`

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

#### `class enum.auto`

Экземпляры заменяются соответствующим значением для членов перечисления.

Новое в версии 3.6: `Flag`, `IntFlag`, `auto`

## 8.13.2. Создание перечисления

Перечисления создаются с использованием синтаксиса [`class`](https://python-all.ru/3.6/reference/compound_stmts.html#class), что делает их легко читаемыми и удобными для записи. Альтернативный метод создания описан в [Функциональный API](https://python-all.ru/3.6/library/enum.html#functional-api). Чтобы определить перечисление, создайте подкласс [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) следующим образом:

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

> **Примечание**
>
> Значения членов Enum
>
> Значения членов могут быть любыми: [`int`](https://python-all.ru/3.6/library/functions.html#int), [`str`](https://python-all.ru/3.6/library/stdtypes.html#str) и т.д. Если точное значение неважно, можно использовать экземпляры [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto), и подходящее значение будет выбрано автоматически. Следует соблюдать осторожность при смешивании [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto) с другими значениями.

> **Примечание**
>
> Терминология
>
> - Класс `Color` является *перечислением* (или *enum*)
> - Атрибуты `Color.RED`, `Color.GREEN` и т.д. являются *членами перечисления* (или *членами перечисления*) и по сути являются константами.
> - Члены перечисления имеют *имена* и *значения* (имя `Color.RED` – `RED`, значение `Color.BLUE` – `3` и т.д.)

> **Примечание**
>
> Хотя для создания перечислений мы используем синтаксис [`class`](https://python-all.ru/3.6/reference/compound_stmts.html#class), перечисления не являются обычными классами Python. Подробнее см. [Чем отличаются перечисления?](https://python-all.ru/3.6/library/enum.html#how-are-enums-different).

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

```python
>>> print(Color.RED)
Color.RED
```

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

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

*Тип* члена перечисления – это перечисление, к которому он принадлежит:

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

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

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

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

```python
>>> 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
```

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

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

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

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

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

Если требуется обратиться к членам перечисления по *имени*, используйте доступ по ключу:

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

Если есть член перечисления и требуется его `name` или `value`:

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

## 8.13.4. Дублирование членов и значений перечисления

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

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

Однако двум членам перечисления разрешено иметь одинаковое значение. Если два члена A и B имеют одинаковое значение (и A определён первым), то B является псевдонимом A. Поиск по значению A и B вернёт A. Поиск по имени B также вернёт A:

```python
>>> 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>
```

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

## 8.13.5. Обеспечение уникальности значений перечисления

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

#### `@enum.unique`

Декоратор [`class`](https://python-all.ru/3.6/reference/compound_stmts.html#class), предназначенный специально для перечислений. Он просматривает `__members__` перечисления, собирая все найденные псевдонимы; если таковые обнаружены, возбуждается [`ValueError`](https://python-all.ru/3.6/library/exceptions.html#ValueError) с подробностями.

```python
>>> 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
```

## 8.13.6. Использование автоматических значений

Если точное значение неважно, можно использовать [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto):

```python
>>> 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_()`, который можно переопределить:

```python
>>> 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`](https://python-all.ru/3.6/library/functions.html#int) в последовательности с последним указанным [`int`](https://python-all.ru/3.6/library/functions.html#int), но то, как это делается, является деталью реализации и может измениться.

## 8.13.7. Итерация

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

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

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

```python
>>> 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__` можно использовать для детального программного доступа к членам перечисления. Например, для поиска всех псевдонимов:

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

## 8.13.8. Сравнения

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

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

Упорядоченные сравнения между значениями перечисления *не* поддерживаются. Члены перечисления – не целые числа (но см. [IntEnum](https://python-all.ru/3.6/library/enum.html#intenum) ниже):

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

Однако сравнения на равенство определены:

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

Сравнения со значениями, не являющимися перечислениями, всегда дают результат «не равно» (опять же, [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) был явно спроектирован для другого поведения, см. ниже):

```python
>>> Color.BLUE == 2
False
```

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

В примерах выше для значений перечислений используются целые числа. Использование целых чисел – это кратко и удобно (и предусмотрено по умолчанию [функциональным API](https://python-all.ru/3.6/library/enum.html#functional-api)), но не является строгим требованием. В подавляющем большинстве случаев не важно, каково фактическое значение элемента перечисления. Но если значение *является* важным, перечисления могут иметь произвольные значения.

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

```python
>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self – это член перечисления
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls здесь – это перечисление
...         return cls.HAPPY
...
```

Тогда:

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

Правила допустимости следующие: имена, начинающиеся и заканчивающиеся одним подчеркиванием, зарезервированы для перечисления и не могут использоваться; все остальные атрибуты, определенные в перечислении, становятся его членами, за исключением специальных методов ([`__str__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__str__), [`__add__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__add__) и т. д.) и дескрипторов (методы также являются дескрипторами).

Примечание: если в перечислении определены [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) и/или [`__init__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__init__), то все значения, переданные элементу перечисления, будут переданы в эти методы. В качестве примера см. [Planet](https://python-all.ru/3.6/library/enum.html#planet).

## 8.13.10. Ограниченное создание подклассов перечислений

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

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

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

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

Разрешение создания подклассов перечислений, определяющих члены, привело бы к нарушению некоторых важных инвариантов типов и экземпляров. С другой стороны, имеет смысл разрешить общее поведение для группы перечислений. (Пример см. в [OrderedEnum](https://python-all.ru/3.6/library/enum.html#orderedenum).)

## 8.13.11. Сериализация (pickling)

Перечисления могут быть упакованы и распакованы с помощью модуля pickle.

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

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

> **Примечание**
>
> С протоколом pickle версии 4 можно легко упаковывать перечисления, вложенные в другие классы.

Можно изменить способ сериализации/десериализации членов Enum, определив [`__reduce_ex__()`](https://python-all.ru/3.6/library/pickle.html#object.__reduce_ex__) в классе перечисления.

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

Класс [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) является вызываемым и предоставляет следующее функциональное API:

```python
>>> 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`](https://python-all.ru/3.6/library/collections.html#collections.namedtuple). Первый аргумент вызова [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) – это имя перечисления.

Второй аргумент – это *источник* имён членов перечисления. Это может быть строка имён, разделённых пробелами, последовательность имён, последовательность двухэлементных кортежей с парами ключ/значение или отображение (например, словарь) имён на значения. Последние два варианта позволяют присваивать произвольные значения перечислениям; остальные автоматически присваивают увеличивающиеся целые числа, начиная с 1 (используйте параметр `start` для указания другого начального значения). Возвращается новый класс, производный от [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum). Другими словами, приведённое выше присваивание `Animal` эквивалентно:

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

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

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

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

> **Предупреждение**
>
> Если `module` не указан, и Enum не может его определить, новые члены Enum не будут поддерживать распаковку; чтобы ошибки были ближе к источнику, pickling будет отключён.

Новый протокол pickle 4 также в некоторых обстоятельствах полагается на то, что [`__qualname__`](https://python-all.ru/3.6/library/stdtypes.html#definition.__qualname__) установлен в расположение, где pickle сможет найти класс. Например, если класс стал доступен в классе SomeData в глобальной области видимости:

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

Полная сигнатура:

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

**значение**

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

**имена**

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

```python
'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
```

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

```python
['RED', 'GREEN', 'BLUE']
```

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

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

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

```python
{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
```

**модуль**

имя модуля, в котором находится новый класс Enum.

**qualname**

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

**тип**

тип, который следует примешать к новому классу Enum.

**start**

число, с которого начинать отсчёт, если переданы только имена.

Изменено в версии 3.5: Добавлен параметр *start*.

## 8.13.13. Производные перечисления

### 8.13.13.1. IntEnum

Первая разновидность [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) также является подклассом [`int`](https://python-all.ru/3.6/library/functions.html#int). Элементы [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) можно сравнивать с целыми числами; соответственно, целочисленные перечисления разных типов также можно сравнивать друг с другом:

```python
>>> 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`](https://python-all.ru/3.6/library/enum.html#enum.Enum):

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

Значения [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) ведут себя как целые числа и в других ожидаемых аспектах:

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

### 8.13.13.2. IntFlag

Следующая вариация [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum), а именно [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag), также основана на [`int`](https://python-all.ru/3.6/library/functions.html#int). Разница в том, что члены [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) можно объединять с помощью побитовых операторов (&, |, ^, ~), и результат всё ещё остаётся членом [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag). Однако, как следует из названия, члены [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) также являются подклассами [`int`](https://python-all.ru/3.6/library/functions.html#int) и могут использоваться везде, где используется [`int`](https://python-all.ru/3.6/library/functions.html#int). Любая операция над членом [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag), кроме побитовых операций, приведёт к потере его принадлежности к [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag).

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

Пример класса [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag):

```python
>>> 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
```

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

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

Ещё одно важное отличие между [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) и [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) в том, что если ни один флаг не установлен (значение равно 0), то его логическое значение – [`False`](https://python-all.ru/3.6/library/constants.html#False):

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

Поскольку члены [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) также являются подклассами [`int`](https://python-all.ru/3.6/library/functions.html#int), их можно объединять с ними:

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

### 8.13.13.3. Flag

Последняя разновидность – [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag). Как и [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag), элементы [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag) можно комбинировать с помощью побитовых операторов (&, |, ^, ~). В отличие от [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag), их нельзя комбинировать или сравнивать ни с каким другим перечислением [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag), а также с [`int`](https://python-all.ru/3.6/library/functions.html#int). Хотя можно задавать значения напрямую, рекомендуется использовать [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto) в качестве значения и позволить [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag) выбрать подходящее значение.

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

Как и в случае с [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag), если комбинация элементов [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag) не содержит установленных флагов, логическое значение равно [`False`](https://python-all.ru/3.6/library/constants.html#False):

```python
>>> 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, …), в то время как комбинации флагов – нет:

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

Присвоение имени условию «флаги не установлены» не меняет его логическое значение:

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

> **Примечание**
>
> Для большинства нового кода настоятельно рекомендуются [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) и [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag), поскольку [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) и [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) нарушают некоторые семантические обещания перечисления (будучи сравнимыми с целыми числами, а значит, по транзитивности, и с другими не связанными перечислениями). [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) и [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) следует использовать только в тех случаях, когда [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) и [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag) не подходят; например, при замене целочисленных констант на перечисления или для взаимодействия с другими системами.

### 8.13.13.4. Прочие

Хотя [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) является частью модуля [`enum`](https://python-all.ru/3.6/library/enum.html#module-enum), его очень просто реализовать самостоятельно:

```python
class IntEnum(int, Enum):
    pass
```

Это показывает, как можно определить подобные производные перечисления; например, `StrEnum`, в который примешивается [`str`](https://python-all.ru/3.6/library/stdtypes.html#str) вместо [`int`](https://python-all.ru/3.6/library/functions.html#int).

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

1. При создании подкласса [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) типы-примеси должны располагаться перед самим [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) в последовательности базовых классов, как в [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) примере выше.
2. Хотя [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) может содержать элементы любого типа, как только к нему примешивается дополнительный тип, все элементы должны иметь значения этого типа, например [`int`](https://python-all.ru/3.6/library/functions.html#int) выше. Это ограничение не распространяется на примеси, которые только добавляют методы и не указывают другой тип данных, такие как [`int`](https://python-all.ru/3.6/library/functions.html#int) или [`str`](https://python-all.ru/3.6/library/stdtypes.html#str).
3. При примешивании другого типа данных атрибут `value` *отличается* от самого члена перечисления, хотя он эквивалентен и будет равным при сравнении.
4. Форматирование в стиле %: *%s* и *%r* вызывают методы [`__str__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__str__) и [`__repr__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__repr__) класса [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) соответственно; другие коды (например, *%i* или *%h* для IntEnum) обрабатывают член перечисления как его примешанный тип.
5. [Форматированные строковые литералы](https://python-all.ru/3.6/reference/lexical_analysis.html#f-strings), [`str.format()`](https://python-all.ru/3.6/library/stdtypes.html#str.format), и [`format()`](https://python-all.ru/3.6/library/functions.html#format) будут использовать [`__format__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__format__) примешанного типа. Если требуется [`str()`](https://python-all.ru/3.6/library/stdtypes.html#str) или [`repr()`](https://python-all.ru/3.6/library/functions.html#repr) класса [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum), используйте коды форматирования *!s* или *!r*.

## 8.13.14. Интересные примеры

Хотя ожидается, что [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum), [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum), [`IntFlag`](https://python-all.ru/3.6/library/enum.html#enum.IntFlag) и [`Flag`](https://python-all.ru/3.6/library/enum.html#enum.Flag) покрывают большинство случаев использования, они не могут охватить их все. Ниже приведены рецепты для некоторых других типов перечислений, которые можно использовать напрямую или как примеры для создания собственных.

### 8.13.14.1. Пропуск значений

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

- использовать экземпляры [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto) в качестве значения
- использовать экземпляры [`object`](https://python-all.ru/3.6/library/functions.html#object) в качестве значения
- использовать описательную строку в качестве значения
- использовать кортеж в качестве значения и собственный [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) для замены кортежа на значение [`int`](https://python-all.ru/3.6/library/functions.html#int)

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

Какой бы метод ни был выбран, следует предоставить [`repr()`](https://python-all.ru/3.6/library/functions.html#repr), который также скрывает (неважное) значение:

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

#### 8.13.14.1.1. Использование [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto)

Использование [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto) будет выглядеть так:

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

#### 8.13.14.1.2. Использование [`object`](https://python-all.ru/3.6/library/functions.html#object)

Использование [`object`](https://python-all.ru/3.6/library/functions.html#object) будет выглядеть так:

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

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

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

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

#### 8.13.14.1.4. Использование настраиваемого [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__)

Использование автонумерующего [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) будет выглядеть так:

```python
>>> 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__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__), если он определён, используется во время создания членов Enum; затем он заменяется методом [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) класса Enum, который используется после создания класса для поиска существующих членов.

### 8.13.14.2. OrderedEnum

Упорядоченное перечисление, которое не основано на [`IntEnum`](https://python-all.ru/3.6/library/enum.html#enum.IntEnum) и поэтому сохраняет обычные инварианты [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) (например, несравнимость с другими перечислениями):

```python
>>> 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
```

### 8.13.14.3. DuplicateFreeEnum

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

```python
>>> 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()`](https://python-all.ru/3.6/library/enum.html#enum.unique).

### 8.13.14.4. Planet

Если определены [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) или [`__init__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__init__), значение члена перечисления будет передано этим методам:

```python
>>> 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       # в килограммах
...         self.radius = radius   # в метрах
...     @property
...     def surface_gravity(self):
...         # гравитационная постоянная (м3 кг-1 с-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
```

## 8.13.15. Чем отличаются перечисления?

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

### 8.13.15.1. Классы перечислений

Мета-класс `EnumMeta` отвечает за предоставление [`__contains__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__contains__), [`__dir__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__dir__), [`__iter__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__iter__) и других методов, которые позволяют выполнять с классом [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) действия, которые на обычном классе не сработают, например *list(Color)* или *some\_var in Color*. `EnumMeta` отвечает за обеспечение того, чтобы различные другие методы итогового класса [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) были корректны (таких как [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__), [`__getnewargs__()`](https://python-all.ru/3.6/library/pickle.html#object.__getnewargs__), [`__str__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__str__) и [`__repr__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__repr__)).

### 8.13.15.2. Элементы перечисления (также экземпляры)

Самое интересное в членах Enum – это то, что они являются синглтонами. `EnumMeta` создаёт их все при создании самого класса [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum), а затем помещает на место настраиваемый [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__), чтобы гарантировать, что новые экземпляры никогда не будут созданы, возвращая только существующие экземпляры членов.

### 8.13.15.3. Тонкости

#### 8.13.15.3.1. Поддерживаемые `__dunder__` имена

`__members__` – это `OrderedDict` из `member_name`:`member` элементов. Он доступен только у класса.

[`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__), если указан, должен создавать и возвращать элементы перечисления; также рекомендуется соответствующим образом установить `_value_` элемента. После создания всех элементов он больше не используется.

#### 8.13.15.3.2. Поддерживаемые `_sunder_` имена

- `_name_` – имя члена
- `_value_` – значение элемента; может быть установлено / изменено в `__new__`
- `_missing_` – функция поиска, используемая, когда значение не найдено; может быть переопределена
- `_order_` – используется в коде для Python 2/3 для обеспечения согласованности порядка элементов (атрибут класса, удаляется при создании класса)
- `_generate_next_value_` – используется [функциональным API](https://python-all.ru/3.6/library/enum.html#functional-api) и [`auto`](https://python-all.ru/3.6/library/enum.html#enum.auto) для получения подходящего значения для элемента перечисления; может быть переопределён

Новое в версии 3.6: `_missing_`, `_order_`, `_generate_next_value_`

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

```python
>>> 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_` необходим, поскольку порядок определения теряется до того, как его можно зафиксировать.

#### 8.13.15.3.3. `Enum` тип элемента

Члены [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) являются экземплярами своего класса [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) и обычно доступны как `EnumClass.member`. При определённых обстоятельствах к ним также можно обращаться как `EnumClass.member.member`, но никогда не следует этого делать, так как такой поиск может завершиться неудачей или, что ещё хуже, вернуть что-то кроме искомого члена [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) (это ещё одна веская причина использовать для членов имена в верхнем регистре):

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

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

#### 8.13.15.3.4. Булево значение `Enum` классов и элементов

Члены [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum), смешанные с не-[`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) типами (такими как [`int`](https://python-all.ru/3.6/library/functions.html#int), [`str`](https://python-all.ru/3.6/library/stdtypes.html#str) и т.п.), вычисляются по правилам примешанного типа; в противном случае все члены вычисляются как [`True`](https://python-all.ru/3.6/library/constants.html#True). Чтобы логическое вычисление вашего собственного Enum зависело от значения члена, добавьте в свой класс следующее:

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

Классы [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) всегда вычисляются как [`True`](https://python-all.ru/3.6/library/constants.html#True).

#### 8.13.15.3.5. `Enum` классы с методами

Если добавить подклассу [`Enum`](https://python-all.ru/3.6/library/enum.html#enum.Enum) дополнительные методы, как в классе [Planet](https://python-all.ru/3.6/library/enum.html#planet) выше, эти методы будут видны в [`dir()`](https://python-all.ru/3.6/library/functions.html#dir) члена, но не класса:

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

#### 8.13.15.3.6. Объединение элементов `Flag`

Если комбинация членов Flag не имеет имени, [`repr()`](https://python-all.ru/3.6/library/functions.html#repr) будет включать все именованные флаги и все именованные комбинации флагов, присутствующие в значении:

```python
>>> 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>
```
