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

---

# HOWTO по перечислениям

Перечисление [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) – это набор символьных имён, привязанных к уникальным значениям. Они похожи на глобальные переменные, но предоставляют более полезные [`repr()`](https://python-all.ru/3.16/library/functions.html#repr), группировку, типобезопасность и некоторые другие возможности.

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

```python
>>> from enum import Enum
>>> class Weekday(Enum):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 3
...     THURSDAY = 4
...     FRIDAY = 5
...     SATURDAY = 6
...     SUNDAY = 7
```

Или, возможно, основные цвета RGB:

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

Как видите, создать [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) так же просто, как написать класс, наследующий от самого `Enum`.

> **Примечание**
>
> Регистр имён членов перечисления
>
> Поскольку перечисления используются для представления констант, и чтобы избежать проблем с конфликтами имён между методами/атрибутами примесных классов и именами перечислений, мы настоятельно рекомендуем использовать имена в ВЕРХНЕМ\_РЕГИСТРЕ для членов и будем использовать этот стиль в наших примерах.

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

```python
>>> Weekday(3)
<Weekday.WEDNESDAY: 3>
```

Как видите, `repr()` члена показывает имя перечисления, имя члена и значение. `str()` члена показывает только имя перечисления и имя члена:

```python
>>> print(Weekday.THURSDAY)
Weekday.THURSDAY
```

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

```python
>>> type(Weekday.MONDAY)
<enum 'Weekday'>
>>> isinstance(Weekday.FRIDAY, Weekday)
True
```

У членов перечисления есть атрибут, содержащий только их `name`:

```python
>>> print(Weekday.TUESDAY.name)
TUESDAY
```

Аналогично, у них есть атрибут для их `value`:

```python
>>> Weekday.WEDNESDAY.value
3
```

В отличие от многих языков, которые рассматривают перечисления только как пары имя/значение, перечисления Python могут иметь добавленное поведение. Например, [`datetime.date`](https://python-all.ru/3.16/library/datetime.html#datetime.date) имеет два метода для получения дня недели: [`weekday()`](https://python-all.ru/3.16/library/datetime.html#datetime.date.weekday) и [`isoweekday()`](https://python-all.ru/3.16/library/datetime.html#datetime.date.isoweekday). Разница в том, что один из них считает от 0 до 6, а другой – от 1 до 7. Вместо того чтобы отслеживать это самостоятельно, мы можем добавить метод в перечисление `Weekday` для извлечения дня из экземпляра `date` и возврата соответствующего члена перечисления:

```python
@classmethod
def from_date(cls, date):
    return cls(date.isoweekday())
```

Полное перечисление `Weekday` теперь выглядит так:

```python
>>> class Weekday(Enum):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 3
...     THURSDAY = 4
...     FRIDAY = 5
...     SATURDAY = 6
...     SUNDAY = 7
...     #
...     @classmethod
...     def from_date(cls, date):
...         return cls(date.isoweekday())
```

Теперь мы можем узнать, какой сегодня день! Смотрите:

```python
>>> import datetime as dt
>>> Weekday.from_date(dt.date.today())
<Weekday.TUESDAY: 2>
```

Конечно, если вы читаете это в другой день, вы увидите этот день вместо.

Это перечисление `Weekday` отлично подходит, если нашей переменной нужен только один день, но что если нужно несколько? Возможно, мы пишем функцию для планирования дел на неделю и не хотим использовать [`list`](https://python-all.ru/3.16/library/stdtypes.html#list) – мы могли бы использовать другой тип [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum):

```python
>>> from enum import Flag
>>> class Weekday(Flag):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 4
...     THURSDAY = 8
...     FRIDAY = 16
...     SATURDAY = 32
...     SUNDAY = 64
```

Мы изменили две вещи: мы наследуем от [`Flag`](https://python-all.ru/3.16/library/enum.html#enum.Flag), и все значения являются степенями двойки.

Так же, как и в исходном перечислении `Weekday` выше, мы можем иметь одиночный выбор:

```python
>>> first_week_day = Weekday.MONDAY
>>> first_week_day
<Weekday.MONDAY: 1>
```

Но [`Flag`](https://python-all.ru/3.16/library/enum.html#enum.Flag) также позволяет нам объединить несколько членов в одну переменную:

```python
>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
>>> weekend
<Weekday.SATURDAY|SUNDAY: 96>
```

Можно даже перебирать переменную [`Flag`](https://python-all.ru/3.16/library/enum.html#enum.Flag):

```python
>>> for day in weekend:
...     print(day)
Weekday.SATURDAY
Weekday.SUNDAY
```

Итак, давайте настроим несколько задач:

```python
>>> chores_for_ethan = {
...     'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY,
...     'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY,
...     'answer SO questions': Weekday.SATURDAY,
...     }
```

И функция для отображения задач на заданный день:

```python
>>> def show_chores(chores, day):
...     for chore, days in chores.items():
...         if day in days:
...             print(chore)
...
>>> show_chores(chores_for_ethan, Weekday.SATURDAY)
answer SO questions
```

В случаях, когда фактические значения членов не важны, можно сэкономить время и использовать [`auto()`](https://python-all.ru/3.16/library/enum.html#enum.auto) для значений:

```python
>>> from enum import auto
>>> class Weekday(Flag):
...     MONDAY = auto()
...     TUESDAY = auto()
...     WEDNESDAY = auto()
...     THURSDAY = auto()
...     FRIDAY = auto()
...     SATURDAY = auto()
...     SUNDAY = auto()
...     WEEKEND = SATURDAY | SUNDAY
```

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

Иногда бывает полезно обращаться к членам перечисления программно (например, в ситуациях, когда `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
```

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

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

```python
>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: 'SQUARE' already defined as 2
```

Однако член перечисления может иметь и другие связанные с ним имена. Если имеются две записи `A` и `B` с одинаковым значением (и `A` определён первой), то `B` является псевдонимом для члена `A`. Поиск по значению значения `A` вернёт член `A`. Поиск по имени `A` вернёт член `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>
```

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

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

По умолчанию перечисления допускают несколько имён в качестве псевдонимов одного и того же значения. Если такое поведение нежелательно, можно использовать декоратор [`@unique`](https://python-all.ru/3.16/library/enum.html#enum.unique):

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

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

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

```python
>>> from enum import Enum, auto
>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> [member.value for member in Color]
[1, 2, 3]
```

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

```python
>>> class AutoName(Enum):
...     @staticmethod
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> [member.value for member in Ordinal]
['NORTH', 'SOUTH', 'EAST', 'WEST']
```

> **Примечание**
>
> Метод [`_generate_next_value_()`](https://python-all.ru/3.16/library/enum.html#enum.Enum._generate_next_value_) должен быть определён до любых членов.

## Перебор

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

```python
>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
>>> list(Weekday)
[<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]
```

Обратите внимание, что псевдонимы `Shape.ALIAS_FOR_SQUARE` и `Weekday.WEEKEND` не отображаются.

Специальный атрибут `__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']
```

> **Примечание**
>
> Псевдонимы для флагов включают значения с несколькими установленными флагами, например `3`, и без установленных флагов, т.е. `0`.

## Сравнения

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

```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.16/howto/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
```

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

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

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

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

Большинство примеров выше используют целые числа для значений перечисления. Использование целых чисел кратко и удобно (и предоставляется по умолчанию [функциональным API](https://python-all.ru/3.16/howto/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'
```

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

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

> **Примечание**
>
> Метод [`__new__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__new__), если определён, используется при создании членов Enum; затем он заменяется методом `__new__()` Enum, который используется после создания класса для поиска существующих членов. Подробнее см. [Когда использовать \_\_new\_\_() vs. \_\_init\_\_()](https://python-all.ru/3.16/howto/enum.html#new-vs-init).

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

Новый класс [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) должен иметь один базовый класс перечисления, не более одного конкретного типа данных и столько миксин-классов на основе [`object`](https://python-all.ru/3.16/library/functions.html#object), сколько нужно. Порядок этих базовых классов:

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

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

```python
>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>
```

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

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

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

## Поддержка dataclass

При наследовании от [`dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass), [`__repr__()`](https://python-all.ru/3.16/library/enum.html#enum.Enum.__repr__) опускает имя унаследованного класса. Например:

```python
>>> from dataclasses import dataclass, field
>>> @dataclass
... class CreatureDataMixin:
...     size: str
...     legs: int
...     tail: bool = field(repr=False, default=True)
...
>>> class Creature(CreatureDataMixin, Enum):
...     BEETLE = 'small', 6
...     DOG = 'medium', 4
...
>>> Creature.DOG
<Creature.DOG: size='medium', legs=4>
```

Используйте аргумент [`dataclass()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) `repr=False` для использования стандартного [`repr()`](https://python-all.ru/3.16/library/functions.html#repr).

Изменено в версии 3.12: В области значений отображаются только поля dataclass, а не имя dataclass.

> **Примечание**
>
> Добавление декоратора [`@~dataclasses.dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) к [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) и его подклассам не поддерживается. Ошибок это не вызовет, но приведёт к очень странным результатам во время выполнения, например, члены будут равны друг другу:
>
> ```python
> >>> @dataclass               # не делайте так: это не имеет смысла
> ... class Color(Enum):
> ...    RED = 1
> ...    BLUE = 2
> ...
> >>> Color.RED is Color.BLUE
> False
> >>> Color.RED == Color.BLUE  # проблема здесь: они не должны быть равны
> True
> ```

## 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 можно легко упаковывать перечисления, вложенные в другие классы.

Можно изменить способ упаковки/распаковки членов перечисления, определив [`__reduce_ex__()`](https://python-all.ru/3.16/library/pickle.html#object.__reduce_ex__) в классе перечисления. Метод по умолчанию – по значению (by-value), но перечисления со сложными значениями могут захотеть использовать по имени (by-name):

```python
>>> import enum
>>> class MyEnum(enum.Enum):
...     __reduce_ex__ = enum.pickle_by_enum_name
```

> **Примечание**
>
> Использование по имени для флагов не рекомендуется, так как безымянные псевдонимы не будут распаковываться.

## Функциональное API

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

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

Семантика этого API напоминает [`namedtuple`](https://python-all.ru/3.16/library/collections.html#collections.namedtuple). Первый аргумент вызова [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) – это имя перечисления.

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

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

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

Pickling перечислений, созданных с помощью функционального 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.16/reference/datamodel.html#type.__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,
    )
```

- *value*: Что новый класс перечисления будет записывать в качестве своего имени.
- *names*: Члены перечисления. Это может быть строка, разделённая пробелами или запятыми (значения будут начинаться с 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}
  ```
- *module*: имя модуля, в котором можно найти новый класс перечисления.
- *qualname*: положение нового класса перечисления в модуле.
- *type*: тип, подмешиваемый к новому классу перечисления.
- *start*: число, с которого начинать отсчёт, если переданы только имена.

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

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

### IntEnum

Первая разновидность [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) также является подклассом [`int`](https://python-all.ru/3.16/library/functions.html#int). Элементы [`IntEnum`](https://python-all.ru/3.16/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.16/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.16/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]
```

### StrEnum

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

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

### IntFlag

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

> **Примечание**
>
> Любая операция с элементом [`IntFlag`](https://python-all.ru/3.16/library/enum.html#enum.IntFlag), кроме побитовых, приведёт к потере принадлежности к `IntFlag`.
>
> Побитовые операции, результатом которых являются недопустимые значения [`IntFlag`](https://python-all.ru/3.16/library/enum.html#enum.IntFlag), приводят к потере принадлежности к `IntFlag`. Подробнее см. [`FlagBoundary`](https://python-all.ru/3.16/library/enum.html#enum.FlagBoundary).

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

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

Пример класса [`IntFlag`](https://python-all.ru/3.16/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: 0>
>>> Perm(7)
<Perm.RWX: 7>
```

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

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

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

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

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

```python
>>> Perm.X | 4
<Perm.R|X: 5>

>>> Perm.X + 8
9
```

> **Примечание**
>
> Оператор отрицания `~` всегда возвращает элемент [`IntFlag`](https://python-all.ru/3.16/library/enum.html#enum.IntFlag) с положительным значением:
>
> ```python
> >>> (~Perm.X).value == (Perm.R|Perm.W).value == 6
> True
> ```

По элементам [`IntFlag`](https://python-all.ru/3.16/library/enum.html#enum.IntFlag) также можно выполнять итерацию:

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

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

### Flag

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

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

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

Члены [`Flag`](https://python-all.ru/3.16/library/enum.html#enum.Flag) также можно перебирать:

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

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

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

### Другие

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

```python
class IntEnum(int, ReprEnum):   # или Enum вместо ReprEnum
    pass
```

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

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

1. При наследовании от [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) типы, примешиваемые (mix-in), должны быть указаны перед самим классом `Enum` в списке базовых классов, как в примере [`IntEnum`](https://python-all.ru/3.16/library/enum.html#enum.IntEnum) выше.
2. Примешиваемые типы должны быть наследуемыми. Например, [`bool`](https://python-all.ru/3.16/library/functions.html#bool) и [`range`](https://python-all.ru/3.16/library/stdtypes.html#range) не являются наследуемыми и приведут к ошибке во время создания Enum, если используются в качестве примешиваемого типа.
3. Хотя [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) может содержать члены любого типа, после примешивания дополнительного типа все члены должны иметь значения этого типа, например [`int`](https://python-all.ru/3.16/library/functions.html#int) выше. Это ограничение не распространяется на примеси, которые только добавляют методы и не задают другой тип.
4. При примешивании другого типа данных атрибут [`value`](https://python-all.ru/3.16/library/enum.html#enum.Enum.value) *отличается* от самого члена перечисления, хотя он эквивалентен и будет равным при сравнении.
5. `data type` – это примесь (mixin), которая определяет [`__new__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__new__), или [`dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass)
6. Форматирование в стиле %: `%s` и `%r` вызывают [`__str__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__str__) и [`__repr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__repr__) класса [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum) соответственно; другие коды (например, `%i` или `%h` для IntEnum) обрабатывают член перечисления как его примешанный тип.
7. [Форматированные строковые литералы](https://python-all.ru/3.16/reference/lexical_analysis.html#f-strings), [`str.format()`](https://python-all.ru/3.16/library/stdtypes.html#str.format) и [`format()`](https://python-all.ru/3.16/library/functions.html#format) будут использовать метод [`__str__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__str__) перечисления.

> **Примечание**
>
> Поскольку [`IntEnum`](https://python-all.ru/3.16/library/enum.html#enum.IntEnum), [`IntFlag`](https://python-all.ru/3.16/library/enum.html#enum.IntFlag) и [`StrEnum`](https://python-all.ru/3.16/library/enum.html#enum.StrEnum) предназначены для замены существующих констант «на лету», их метод [`__str__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__str__) был переопределён на метод `__str__()` их типа данных.

## Когда использовать [`__new__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__new__) или [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__)

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

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

```python
>>> class Coordinate(bytes, Enum):
...     """
...     Согласование с двоичными кодами, которые можно индексировать по целочисленному коду.
...     """
...     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
```

> **Предупреждение**
>
> *Не вызывайте* `super().__new__()`, поскольку будет найден `__new__`, предназначенный только для поиска; вместо этого используйте непосредственно тип данных.

## Тонкие моменты

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

Поддерживаемые имена `__dunder__` и `_sunder_` можно найти в [документации API Enum](https://python-all.ru/3.16/library/enum.html#enum-dunder-sunder).

### \_Private\_\_names

[Приватные имена](https://python-all.ru/3.16/reference/expressions.html#private-name-mangling) не преобразуются в члены перечисления, а остаются обычными атрибутами.

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

### Тип члена `Enum`

Члены перечисления являются экземплярами своего класса перечисления и обычно доступны как `EnumClass.member`. В определённых ситуациях, например, при написании пользовательского поведения перечисления, возможность прямого обращения одного члена к другому бывает полезна и поддерживается; однако во избежание конфликтов имён между именами членов и атрибутами/методами из примешанных классов настоятельно рекомендуется использовать имена в верхнем регистре.

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

### Создание членов, примешанных к другим типам данных

При наследовании от других типов данных, таких как [`int`](https://python-all.ru/3.16/library/functions.html#int) или [`str`](https://python-all.ru/3.16/library/stdtypes.html#str), в сочетании с [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum), все значения после `=` передаются конструктору этого типа данных. Например:

```python
>>> class MyEnum(IntEnum):      # help(int) -> int(x, base=10) -> integer
...     example = '11', 16      # итак, x='11' и base=16
...
>>> MyEnum.example.value        # и hex(11) – это...
17
```

### Логическое значение классов и членов `Enum`

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

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

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

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

Если добавить в подкласс перечисления дополнительные методы, например, как в классе [Planet](https://python-all.ru/3.16/howto/enum.html#planet) ниже, эти методы появятся в [`dir()`](https://python-all.ru/3.16/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__', 'mass', 'name', 'radius', 'surface_gravity', 'value']
```

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

Итерация по комбинации элементов [`Flag`](https://python-all.ru/3.16/library/enum.html#enum.Flag) вернёт только те элементы, которые состоят из одного бита:

```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.RED|GREEN|BLUE: 7>
```

### `Flag` и `IntFlag` мелочи

Используя следующий фрагмент для примеров:

```python
>>> class Color(IntFlag):
...     BLACK = 0
...     RED = 1
...     GREEN = 2
...     BLUE = 4
...     PURPLE = RED | BLUE
...     WHITE = RED | GREEN | BLUE
...
```

верно следующее:

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

  ```python
  >>> list(Color.WHITE)
  [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
  ```
- отрицание флага или набора флагов возвращает новый флаг/набор флагов с соответствующим положительным целым значением:

  ```python
  >>> Color.BLUE
  <Color.BLUE: 4>

  >>> ~Color.BLUE
  <Color.RED|GREEN: 3>
  ```
- имена псевдофлагов формируются из имён их элементов:

  ```python
  >>> (Color.RED | Color.GREEN).name
  'RED|GREEN'

  >>> class Perm(IntFlag):
  ...     R = 4
  ...     W = 2
  ...     X = 1
  ...
  >>> (Perm.R & Perm.W).name is None  # фактически Perm(0)
  True
  ```
- многобитовые флаги, также известные как псевдонимы, могут возвращаться в результате операций:

  ```python
  >>> Color.RED | Color.BLUE
  <Color.PURPLE: 5>

  >>> Color(7)  # или Color(-1)
  <Color.WHITE: 7>

  >>> Color(0)
  <Color.BLACK: 0>
  ```
- проверка членства/включения: нулевые флаги всегда считаются содержащимися:

  ```python
  >>> Color.BLACK in Color.WHITE
  True
  ```

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

  ```python
  >>> Color.PURPLE in Color.WHITE
  True

  >>> Color.GREEN in Color.PURPLE
  False
  ```

Появился новый механизм границ, который управляет обработкой выходящих за пределы / недопустимых битов: `STRICT`, `CONFORM`, `EJECT` и `KEEP`:

- STRICT –\> вызывает исключение при получении недопустимых значений
- CONFORM –\> отбрасывает любые недопустимые биты
- EJECT –\> теряет статус Flag и становится обычным int с заданным значением
- KEEP –\> сохраняет дополнительные биты

  - сохраняет статус Flag и дополнительные биты
  - дополнительные биты не отображаются при итерации
  - дополнительные биты отображаются в repr() и str()

По умолчанию для Flag используется `STRICT`, по умолчанию для `IntFlag` – `EJECT`, а по умолчанию для `_convert_` – `KEEP` (см. `ssl.Options` для примера, когда требуется `KEEP`).

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

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

### Классы Enum

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

### Классы Flag

Флаги имеют расширенное представление о псевдонимах: чтобы быть каноническим, значение флага должно быть степенью двойки и не быть дублирующимся именем. Таким образом, в дополнение к определению псевдонима [`Enum`](https://python-all.ru/3.16/library/enum.html#enum.Enum), флаг без значения (также известный как `0`) или с более чем одним значением, являющимся степенью двойки (например, `3`), считается псевдонимом.

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

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

### Члены флага

Члены флага можно перебирать так же, как и класс [`Flag`](https://python-all.ru/3.16/library/enum.html#enum.Flag), и будут возвращены только канонические члены. Например:

```python
>>> list(Color)
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
```

(Обратите внимание, что `BLACK`, `PURPLE` и `WHITE` не отображаются.)

Инвертирование члена флага возвращает соответствующее положительное значение, а не отрицательное. Например:

```python
>>> ~Color.RED
<Color.GREEN|BLUE: 6>
```

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

```python
>>> len(Color.PURPLE)
2
```

## Сборник рецептов Enum

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

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

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

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

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

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

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

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

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

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

```python
>>> class Color(Enum):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN: <object object at 0x...>>
```

Это также хороший пример того, зачем может понадобиться написать собственный [`__repr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__repr__):

```python
>>> class Color(Enum):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...     def __repr__(self):
...         return "<%s.%s>" % (self.__class__.__name__, self._name_)
...
>>> Color.GREEN
<Color.GREEN>
```

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

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

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

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

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

```python
>>> class AutoNumber(Enum):
...     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: 2>
```

Чтобы сделать более универсальный `AutoNumber`, добавьте `*args` в сигнатуру:

```python
>>> class AutoNumber(Enum):
...     def __new__(cls, *args):      # это единственное изменение по сравнению с предыдущим
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...
```

Затем при наследовании от `AutoNumber` можно написать собственный `__init__` для обработки любых дополнительных аргументов:

```python
>>> class Swatch(AutoNumber):
...     def __init__(self, pantone='unknown'):
...         self.pantone = pantone
...     AUBURN = '3497'
...     SEA_GREEN = '1246'
...     BLEACHED_CORAL = () # Новый цвет, кода Pantone пока нет!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'
```

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

> **Предупреждение**
>
> *Не вызывайте* `super().__new__()`, так как будет найден `__new__` только для поиска; вместо этого используйте непосредственно тип данных – например:
>
> ```python
> obj = int.__new__(cls, value)
> ```

### OrderedEnum

Упорядоченное перечисление, которое не основано на [`IntEnum`](https://python-all.ru/3.16/library/enum.html#enum.IntEnum) и поэтому сохраняет обычные инварианты [`Enum`](https://python-all.ru/3.16/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
```

### 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.16/library/enum.html#enum.unique).

### MultiValueEnum

Поддерживает наличие более одного значения для каждого элемента:

```python
>>> class MultiValueEnum(Enum):
...     def __new__(cls, value, *values):
...         self = object.__new__(cls)
...         self._value_ = value
...         for v in values:
...             self._add_value_alias_(v)
...         return self
...
>>> class DType(MultiValueEnum):
...     float32 = 'f', 8
...     double64 = 'd', 9
...
>>> DType('f')
<DType.float32: 'f'>
>>> DType(9)
<DType.double64: 'd'>
```

### Planet

Если определены [`__new__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__new__) или [`__init__()`](https://python-all.ru/3.16/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
```

### TimePeriod

Пример использования атрибута [`_ignore_`](https://python-all.ru/3.16/library/enum.html#enum.Enum._ignore_):

```python
>>> import datetime as dt
>>> class Period(dt.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)>]
```

## Subclassing EnumType

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