enum.md
1> **Источник:** https://python-all.ru/3.12/howto/enum.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# HOWTO по перечислениям89Перечисление [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) – это набор символьных имён, привязанных к уникальным значениям. Они похожи на глобальные переменные, но предоставляют более полезные [`repr()`](https://python-all.ru/3.12/library/functions.html#repr), группировку, типобезопасность и некоторые другие возможности.1011Они наиболее полезны, когда у вас есть переменная, которая может принимать одно из ограниченного набора значений. Например, дни недели:1213```python14>>> from enum import Enum15>>> class Weekday(Enum):16... MONDAY = 117... TUESDAY = 218... WEDNESDAY = 319... THURSDAY = 420... FRIDAY = 521... SATURDAY = 622... SUNDAY = 723```2425Или, возможно, основные цвета RGB:2627```python28>>> from enum import Enum29>>> class Color(Enum):30... RED = 131... GREEN = 232... BLUE = 333```3435Как видите, создать [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) так же просто, как написать класс, наследующий от самого [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum).3637> **Примечание**38>39> Регистр имён членов перечисления40>41> Поскольку перечисления используются для представления констант, и чтобы избежать проблем с конфликтами имён между методами/атрибутами примесных классов и именами перечислений, мы настоятельно рекомендуем использовать имена в ВЕРХНЕМ\_РЕГИСТРЕ для членов и будем использовать этот стиль в наших примерах.4243В зависимости от природы перечисления значение члена может быть важным или нет, но в любом случае это значение можно использовать для получения соответствующего члена:4445```python46>>> Weekday(3)47<Weekday.WEDNESDAY: 3>48```4950Как видите, `repr()` члена показывает имя перечисления, имя члена и значение. `str()` члена показывает только имя перечисления и имя члена:5152```python53>>> print(Weekday.THURSDAY)54Weekday.THURSDAY55```5657*Тип* члена перечисления – это перечисление, которому он принадлежит:5859```python60>>> type(Weekday.MONDAY)61<enum 'Weekday'>62>>> isinstance(Weekday.FRIDAY, Weekday)63True64```6566У членов перечисления есть атрибут, содержащий только их `name`:6768```python69>>> print(Weekday.TUESDAY.name)70TUESDAY71```7273Аналогично, у них есть атрибут для их `value`:7475```python76>>> Weekday.WEDNESDAY.value77378```7980В отличие от многих языков, которые рассматривают перечисления только как пары имя/значение, перечисления Python могут иметь добавленное поведение. Например, [`datetime.date`](https://python-all.ru/3.12/library/datetime.html#datetime.date) имеет два метода для получения дня недели: [`weekday()`](https://python-all.ru/3.12/library/datetime.html#datetime.date.weekday) и [`isoweekday()`](https://python-all.ru/3.12/library/datetime.html#datetime.date.isoweekday). Разница в том, что один из них считает от 0 до 6, а другой – от 1 до 7. Вместо того чтобы отслеживать это самостоятельно, мы можем добавить метод в перечисление `Weekday` для извлечения дня из экземпляра [`date`](https://python-all.ru/3.12/library/datetime.html#datetime.date) и возврата соответствующего члена перечисления:8182```python83@classmethod84def from_date(cls, date):85 return cls(date.isoweekday())86```8788Полное перечисление `Weekday` теперь выглядит так:8990```python91>>> class Weekday(Enum):92... MONDAY = 193... TUESDAY = 294... WEDNESDAY = 395... THURSDAY = 496... FRIDAY = 597... SATURDAY = 698... SUNDAY = 799... #100... @classmethod101... def from_date(cls, date):102... return cls(date.isoweekday())103```104105Теперь мы можем узнать, какой сегодня день! Смотрите:106107```python108>>> from datetime import date109>>> Weekday.from_date(date.today())110<Weekday.TUESDAY: 2>111```112113Конечно, если вы читаете это в другой день, вы увидите этот день вместо.114115Это перечисление `Weekday` отлично подходит, если нашей переменной нужен только один день, но что если нужно несколько? Возможно, мы пишем функцию для планирования дел на неделю и не хотим использовать [`list`](https://python-all.ru/3.12/library/stdtypes.html#list) – мы могли бы использовать другой тип [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum):116117```python118>>> from enum import Flag119>>> class Weekday(Flag):120... MONDAY = 1121... TUESDAY = 2122... WEDNESDAY = 4123... THURSDAY = 8124... FRIDAY = 16125... SATURDAY = 32126... SUNDAY = 64127```128129Мы изменили две вещи: мы наследуем от [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag), и все значения являются степенями двойки.130131Так же, как и в исходном перечислении `Weekday` выше, мы можем иметь одиночный выбор:132133```python134>>> first_week_day = Weekday.MONDAY135>>> first_week_day136<Weekday.MONDAY: 1>137```138139Но [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) также позволяет нам объединить несколько членов в одну переменную:140141```python142>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY143>>> weekend144<Weekday.SATURDAY|SUNDAY: 96>145```146147Можно даже перебирать переменную [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag):148149```python150>>> for day in weekend:151... print(day)152Weekday.SATURDAY153Weekday.SUNDAY154```155156Итак, давайте настроим несколько задач:157158```python159>>> chores_for_ethan = {160... 'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY,161... 'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY,162... 'answer SO questions': Weekday.SATURDAY,163... }164```165166И функция для отображения задач на заданный день:167168```python169>>> def show_chores(chores, day):170... for chore, days in chores.items():171... if day in days:172... print(chore)173...174>>> show_chores(chores_for_ethan, Weekday.SATURDAY)175answer SO questions176```177178В случаях, когда фактические значения членов не важны, можно сэкономить время и использовать [`auto()`](https://python-all.ru/3.12/library/enum.html#enum.auto) для значений:179180```python181>>> from enum import auto182>>> class Weekday(Flag):183... MONDAY = auto()184... TUESDAY = auto()185... WEDNESDAY = auto()186... THURSDAY = auto()187... FRIDAY = auto()188... SATURDAY = auto()189... SUNDAY = auto()190... WEEKEND = SATURDAY | SUNDAY191```192193## Программный доступ к членам перечисления и их атрибутам194195Иногда бывает полезно обращаться к членам перечисления программно (например, в ситуациях, когда `Color.RED` не подходит, поскольку точный цвет неизвестен на момент написания программы). `Enum` предоставляет такой доступ:196197```python198>>> Color(1)199<Color.RED: 1>200>>> Color(3)201<Color.BLUE: 3>202```203204Если требуется обратиться к членам перечисления по *имени*, используйте доступ по ключу:205206```python207>>> Color['RED']208<Color.RED: 1>209>>> Color['GREEN']210<Color.GREEN: 2>211```212213Если есть член перечисления и требуется его `name` или `value`:214215```python216>>> member = Color.RED217>>> member.name218'RED'219>>> member.value2201221```222223## Дублирование членов и значений перечисления224225Наличие двух членов перечисления с одинаковым именем недопустимо:226227```python228>>> class Shape(Enum):229... SQUARE = 2230... SQUARE = 3231...232Traceback (most recent call last):233...234TypeError: 'SQUARE' already defined as 2235```236237Однако член перечисления может иметь и другие связанные с ним имена. Если имеются две записи `A` и `B` с одинаковым значением (и `A` определён первой), то `B` является псевдонимом для члена `A`. Поиск по значению значения `A` вернёт член `A`. Поиск по имени `A` вернёт член `A`. Поиск по имени `B` также вернёт член `A`:238239```python240>>> class Shape(Enum):241... SQUARE = 2242... DIAMOND = 1243... CIRCLE = 3244... ALIAS_FOR_SQUARE = 2245...246>>> Shape.SQUARE247<Shape.SQUARE: 2>248>>> Shape.ALIAS_FOR_SQUARE249<Shape.SQUARE: 2>250>>> Shape(2)251<Shape.SQUARE: 2>252```253254> **Примечание**255>256> Попытка создать член с тем же именем, что и уже определённый атрибут (другой член, метод и т.д.), или попытка создать атрибут с тем же именем, что и член, не допускается.257258## Обеспечение уникальности значений перечисления259260По умолчанию перечисления допускают несколько имён в качестве псевдонимов одного и того же значения. Если такое поведение нежелательно, можно использовать декоратор [`unique()`](https://python-all.ru/3.12/library/enum.html#enum.unique):261262```python263>>> from enum import Enum, unique264>>> @unique265... class Mistake(Enum):266... ONE = 1267... TWO = 2268... THREE = 3269... FOUR = 3270...271Traceback (most recent call last):272...273ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE274```275276## Использование автоматических значений277278Если точное значение неважно, можно использовать [`auto`](https://python-all.ru/3.12/library/enum.html#enum.auto):279280```python281>>> from enum import Enum, auto282>>> class Color(Enum):283... RED = auto()284... BLUE = auto()285... GREEN = auto()286...287>>> [member.value for member in Color]288[1, 2, 3]289```290291Значения выбираются с помощью [`_generate_next_value_()`](https://python-all.ru/3.12/library/enum.html#enum.Enum._generate_next_value_), который можно переопределить:292293```python294>>> class AutoName(Enum):295... @staticmethod296... def _generate_next_value_(name, start, count, last_values):297... return name298...299>>> class Ordinal(AutoName):300... NORTH = auto()301... SOUTH = auto()302... EAST = auto()303... WEST = auto()304...305>>> [member.value for member in Ordinal]306['NORTH', 'SOUTH', 'EAST', 'WEST']307```308309> **Примечание**310>311> Метод [`_generate_next_value_()`](https://python-all.ru/3.12/library/enum.html#enum.Enum._generate_next_value_) должен быть определён до любых членов.312313## Перебор314315Перебор членов перечисления не включает псевдонимы:316317```python318>>> list(Shape)319[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]320>>> list(Weekday)321[<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]322```323324Обратите внимание, что псевдонимы `Shape.ALIAS_FOR_SQUARE` и `Weekday.WEEKEND` не отображаются.325326Специальный атрибут `__members__` – это упорядоченное отображение имён на члены, доступное только для чтения. Он включает все имена, определённые в перечислении, включая псевдонимы:327328```python329>>> for name, member in Shape.__members__.items():330... name, member331...332('SQUARE', <Shape.SQUARE: 2>)333('DIAMOND', <Shape.DIAMOND: 1>)334('CIRCLE', <Shape.CIRCLE: 3>)335('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)336```337338Атрибут `__members__` можно использовать для детального программного доступа к членам перечисления. Например, для поиска всех псевдонимов:339340```python341>>> [name for name, member in Shape.__members__.items() if member.name != name]342['ALIAS_FOR_SQUARE']343```344345> **Примечание**346>347> Псевдонимы для флагов включают значения с несколькими установленными флагами, например `3`, и без установленных флагов, т.е. `0`.348349## Сравнения350351Члены перечисления сравниваются по идентичности:352353```python354>>> Color.RED is Color.RED355True356>>> Color.RED is Color.BLUE357False358>>> Color.RED is not Color.BLUE359True360```361362Упорядоченные сравнения между значениями перечисления *не* поддерживаются. Члены перечисления – не целые числа (но см. [IntEnum](https://python-all.ru/3.12/howto/enum.html#intenum) ниже):363364```python365>>> Color.RED < Color.BLUE366Traceback (most recent call last):367 File "<stdin>", line 1, in <module>368TypeError: '<' not supported between instances of 'Color' and 'Color'369```370371Однако сравнения на равенство определены:372373```python374>>> Color.BLUE == Color.RED375False376>>> Color.BLUE != Color.RED377True378>>> Color.BLUE == Color.BLUE379True380```381382Сравнения со значениями, не являющимися перечислениями, всегда дают результат «не равно» (опять же, [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) был явно спроектирован для другого поведения, см. ниже):383384```python385>>> Color.BLUE == 2386False387```388389> **Предупреждение**390>391> Можно перезагружать модули – если перезагруженный модуль содержит перечисления, они будут воссозданы, и новые члены могут не совпадать (по идентичности или равенству) с исходными членами.392393## Допустимые члены и атрибуты перечислений394395Большинство примеров выше используют целые числа для значений перечисления. Использование целых чисел кратко и удобно (и предоставляется по умолчанию [функциональным API](https://python-all.ru/3.12/howto/enum.html#functional-api)), но не является строго обязательным. В подавляющем большинстве случаев неважно, каково фактическое значение перечисления. Но если значение *важно*, перечисления могут иметь произвольные значения.396397Перечисления – это классы Python и могут иметь обычные методы и специальные методы. Если есть такое перечисление:398399```python400>>> class Mood(Enum):401... FUNKY = 1402... HAPPY = 3403...404... def describe(self):405... # self – это член перечисления406... return self.name, self.value407...408... def __str__(self):409... return 'my custom str! {0}'.format(self.value)410...411... @classmethod412... def favorite_mood(cls):413... # cls здесь – это перечисление414... return cls.HAPPY415...416```417418Тогда:419420```python421>>> Mood.favorite_mood()422<Mood.HAPPY: 3>423>>> Mood.HAPPY.describe()424('HAPPY', 3)425>>> str(Mood.FUNKY)426'my custom str! 1'427```428429Правила допустимого таковы: имена, которые начинаются и заканчиваются одним подчёркиванием, зарезервированы для enum и не могут использоваться; все остальные атрибуты, определённые внутри перечисления, становятся его членами, за исключением специальных методов ([`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__), [`__add__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__add__) и т.д.), дескрипторов (методы тоже являются дескрипторами) и имён переменных, перечисленных в [`_ignore_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._ignore_).430431Примечание: если перечисление определяет [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) и/или [`__init__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__init__), любые значения, переданные члену перечисления, будут переданы в эти методы. Пример см. в [Planet](https://python-all.ru/3.12/howto/enum.html#planet).432433> **Примечание**434>435> Метод [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), если определён, используется при создании членов Enum; затем он заменяется методом [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) Enum, который используется после создания класса для поиска существующих членов. Подробнее см. [Когда использовать \_\_new\_\_() vs. \_\_init\_\_()](https://python-all.ru/3.12/howto/enum.html#new-vs-init).436437## Ограниченное создание подклассов Enum438439Новый класс [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) должен иметь один базовый класс перечисления, не более одного конкретного типа данных и столько миксин-классов на основе [`object`](https://python-all.ru/3.12/library/functions.html#object), сколько нужно. Порядок этих базовых классов:440441```python442class EnumName([mix-in, ...,] [data-type,] base-enum):443 pass444```445446Также создание подкласса перечисления разрешено только в том случае, если перечисление не определяет никаких членов. Поэтому это запрещено:447448```python449>>> class MoreColor(Color):450... PINK = 17451...452Traceback (most recent call last):453...454TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>455```456457Но это разрешено:458459```python460>>> class Foo(Enum):461... def some_behavior(self):462... pass463...464>>> class Bar(Foo):465... HAPPY = 1466... SAD = 2467...468```469470Разрешение создания подклассов перечислений, определяющих члены, привело бы к нарушению некоторых важных инвариантов типов и экземпляров. С другой стороны, имеет смысл разрешить общее поведение для группы перечислений. (Пример см. в [OrderedEnum](https://python-all.ru/3.12/howto/enum.html#orderedenum).)471472## Поддержка dataclass473474При наследовании от [`dataclass`](https://python-all.ru/3.12/library/dataclasses.html#dataclasses.dataclass), [`__repr__()`](https://python-all.ru/3.12/library/enum.html#enum.Enum.__repr__) опускает имя унаследованного класса. Например:475476```python477>>> from dataclasses import dataclass, field478>>> @dataclass479... class CreatureDataMixin:480... size: str481... legs: int482... tail: bool = field(repr=False, default=True)483...484>>> class Creature(CreatureDataMixin, Enum):485... BEETLE = 'small', 6486... DOG = 'medium', 4487...488>>> Creature.DOG489<Creature.DOG: size='medium', legs=4>490```491492Используйте аргумент `dataclass()` `repr=False` для использования стандартного [`repr()`](https://python-all.ru/3.12/library/functions.html#repr).493494Изменено в версии 3.12: В области значений отображаются только поля dataclass, а не имя dataclass.495496## Пиклинг497498Перечисления могут быть упакованы и распакованы с помощью модуля pickle.499500```python501>>> from test.test_enum import Fruit502>>> from pickle import dumps, loads503>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))504True505```506507Обычные ограничения для упаковки с помощью модуля pickle применяются: перечисления, которые можно упаковать, должны быть определены на верхнем уровне модуля, так как для распаковки их нужно импортировать из этого модуля.508509> **Примечание**510>511> С протоколом pickle версии 4 можно легко упаковывать перечисления, вложенные в другие классы.512513Можно изменить способ упаковки/распаковки членов перечисления, определив [`__reduce_ex__()`](https://python-all.ru/3.12/library/pickle.html#object.__reduce_ex__) в классе перечисления. Метод по умолчанию – по значению (by-value), но перечисления со сложными значениями могут захотеть использовать по имени (by-name):514515```python516>>> import enum517>>> class MyEnum(enum.Enum):518... __reduce_ex__ = enum.pickle_by_enum_name519```520521> **Примечание**522>523> Использование по имени для флагов не рекомендуется, так как безымянные псевдонимы не будут распаковываться.524525## Функциональное API526527Класс [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) является вызываемым и предоставляет следующее функциональное API:528529```python530>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')531>>> Animal532<enum 'Animal'>533>>> Animal.ANT534<Animal.ANT: 1>535>>> list(Animal)536[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]537```538539Семантика этого API напоминает [`namedtuple`](https://python-all.ru/3.12/library/collections.html#collections.namedtuple). Первый аргумент вызова [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) – это имя перечисления.540541Второй аргумент – это *источник* имён членов перечисления. Это может быть строка имён, разделённых пробелами, последовательность имён, последовательность двухэлементных кортежей с парами ключ/значение или отображение (например, словарь) имён на значения. Последние два варианта позволяют присваивать произвольные значения перечислениям; остальные автоматически присваивают увеличивающиеся целые числа, начиная с 1 (используйте параметр `start` для указания другого начального значения). Возвращается новый класс, производный от [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum). Другими словами, приведённое выше присваивание `Animal` эквивалентно:542543```python544>>> class Animal(Enum):545... ANT = 1546... BEE = 2547... CAT = 3548... DOG = 4549...550```551552Причина выбора `1` в качестве начального числа, а не `0`, заключается в том, что `0` является `False` в логическом смысле, но по умолчанию все члены перечисления вычисляются как `True`.553554Pickling перечислений, созданных с помощью функционального API, может быть сложным, так как используются детали реализации стека фреймов для определения модуля, в котором создаётся перечисление (например, это не сработает, если используется вспомогательная функция в отдельном модуле, и может не работать на IronPython или Jython). Решение – явно указать имя модуля следующим образом:555556```python557>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)558```559560> **Предупреждение**561>562> Если `module` не указан, и Enum не может его определить, новые члены Enum не будут поддерживать распаковку; чтобы ошибки были ближе к источнику, pickling будет отключён.563564Новый протокол pickle 4 также в некоторых обстоятельствах полагается на то, что [`__qualname__`](https://python-all.ru/3.12/reference/datamodel.html#type.__qualname__) установлен в расположение, где pickle сможет найти класс. Например, если класс стал доступен в классе SomeData в глобальной области видимости:565566```python567>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')568```569570Полная сигнатура:571572```python573Enum(574 value='NewEnumName',575 names=<...>,576 *,577 module='...',578 qualname='...',579 type=<mixed-in class>,580 start=1,581 )582```583584- *value*: Что новый класс перечисления будет записывать в качестве своего имени.585- *names*: Члены перечисления. Это может быть строка, разделённая пробелами или запятыми (значения будут начинаться с 1, если не указано иное):586587 ```python588 'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'589 ```590591 или итератор имён:592593 ```python594 ['RED', 'GREEN', 'BLUE']595 ```596597 или итератор пар (имя, значение):598599 ```python600 [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]601 ```602603 или отображение:604605 ```python606 {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}607 ```608- *module*: имя модуля, в котором можно найти новый класс перечисления.609- *qualname*: положение нового класса перечисления в модуле.610- *type*: тип, подмешиваемый к новому классу перечисления.611- *start*: число, с которого начинать отсчёт, если переданы только имена.612613Изменено в версии 3.5: Добавлен параметр *start*.614615## Производные перечисления616617### IntEnum618619Первая разновидность [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) также является подклассом [`int`](https://python-all.ru/3.12/library/functions.html#int). Элементы [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) можно сравнивать с целыми числами; соответственно, целочисленные перечисления разных типов также можно сравнивать друг с другом:620621```python622>>> from enum import IntEnum623>>> class Shape(IntEnum):624... CIRCLE = 1625... SQUARE = 2626...627>>> class Request(IntEnum):628... POST = 1629... GET = 2630...631>>> Shape == 1632False633>>> Shape.CIRCLE == 1634True635>>> Shape.CIRCLE == Request.POST636True637```638639Однако их по-прежнему нельзя сравнивать со стандартными перечислениями [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum):640641```python642>>> class Shape(IntEnum):643... CIRCLE = 1644... SQUARE = 2645...646>>> class Color(Enum):647... RED = 1648... GREEN = 2649...650>>> Shape.CIRCLE == Color.RED651False652```653654Значения [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) ведут себя как целые числа и в других ожидаемых аспектах:655656```python657>>> int(Shape.CIRCLE)6581659>>> ['a', 'b', 'c'][Shape.CIRCLE]660'b'661>>> [i for i in range(Shape.SQUARE)]662[0, 1]663```664665### StrEnum666667Вторая разновидность [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) также является подклассом [`str`](https://python-all.ru/3.12/library/stdtypes.html#str). Элементы [`StrEnum`](https://python-all.ru/3.12/library/enum.html#enum.StrEnum) можно сравнивать со строками; соответственно, строковые перечисления разных типов также можно сравнивать друг с другом.668669Добавлено в версии 3.12.670671### IntFlag672673Следующая разновидность [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) – [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) – также основана на [`int`](https://python-all.ru/3.12/library/functions.html#int). Отличие в том, что элементы [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) можно комбинировать с помощью побитовых операторов (&, |, ^, ~), и результат, по возможности, остаётся элементом [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag). Как и [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum), элементы [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) также являются целыми числами и могут использоваться везде, где используется [`int`](https://python-all.ru/3.12/library/functions.html#int).674675> **Примечание**676>677> Любая операция с элементом [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag), кроме побитовых, приведёт к потере принадлежности к [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag).678>679> Побитовые операции, результатом которых являются недопустимые значения [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag), приводят к потере принадлежности к [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag). Подробнее см. [`FlagBoundary`](https://python-all.ru/3.12/library/enum.html#enum.FlagBoundary).680681Добавлено в версии 3.6.682683Изменено в версии 3.11.684685Пример класса [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag):686687```python688>>> from enum import IntFlag689>>> class Perm(IntFlag):690... R = 4691... W = 2692... X = 1693...694>>> Perm.R | Perm.W695<Perm.R|W: 6>696>>> Perm.R + Perm.W6976698>>> RW = Perm.R | Perm.W699>>> Perm.R in RW700True701```702703Также можно давать имена комбинациям:704705```python706>>> class Perm(IntFlag):707... R = 4708... W = 2709... X = 1710... RWX = 7711...712>>> Perm.RWX713<Perm.RWX: 7>714>>> ~Perm.RWX715<Perm: 0>716>>> Perm(7)717<Perm.RWX: 7>718```719720> **Примечание**721>722> Именованные комбинации считаются псевдонимами. Псевдонимы не отображаются при итерации, но могут быть возвращены при поиске по значению.723724Изменено в версии 3.11.725726Ещё одно важное отличие между [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) и [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) в том, что если ни один флаг не установлен (значение равно 0), то его логическое значение – [`False`](https://python-all.ru/3.12/library/constants.html#False):727728```python729>>> Perm.R & Perm.X730<Perm: 0>731>>> bool(Perm.R & Perm.X)732False733```734735Поскольку элементы [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) также являются подклассами [`int`](https://python-all.ru/3.12/library/functions.html#int), их можно комбинировать с ними (но при этом может быть потеряна принадлежность к [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag):736737```python738>>> Perm.X | 4739<Perm.R|X: 5>740741>>> Perm.X + 87429743```744745> **Примечание**746>747> Оператор отрицания `~` всегда возвращает элемент [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) с положительным значением:748>749> ```python750> >>> (~Perm.X).value == (Perm.R|Perm.W).value == 6751> True752> ```753754Члены [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) также можно перебирать:755756```python757>>> list(RW)758[<Perm.R: 4>, <Perm.W: 2>]759```760761Добавлено в версии 3.12.762763### Flag764765Последняя разновидность – [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag). Как и [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag), элементы [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) можно комбинировать с помощью побитовых операторов (&, |, ^, ~). В отличие от [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag), их нельзя комбинировать или сравнивать ни с каким другим перечислением [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag), а также с [`int`](https://python-all.ru/3.12/library/functions.html#int). Хотя можно задавать значения напрямую, рекомендуется использовать [`auto`](https://python-all.ru/3.12/library/enum.html#enum.auto) в качестве значения и позволить [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) выбрать подходящее значение.766767Добавлено в версии 3.6.768769Как и в случае с [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag), если комбинация элементов [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) не содержит установленных флагов, логическое значение равно [`False`](https://python-all.ru/3.12/library/constants.html#False):770771```python772>>> from enum import Flag, auto773>>> class Color(Flag):774... RED = auto()775... BLUE = auto()776... GREEN = auto()777...778>>> Color.RED & Color.GREEN779<Color: 0>780>>> bool(Color.RED & Color.GREEN)781False782```783784Отдельные флаги должны иметь значения, являющиеся степенями двойки (1, 2, 4, 8, …), а комбинации флагов – нет:785786```python787>>> class Color(Flag):788... RED = auto()789... BLUE = auto()790... GREEN = auto()791... WHITE = RED | BLUE | GREEN792...793>>> Color.WHITE794<Color.WHITE: 7>795```796797Присвоение имени условию «флаги не установлены» не меняет его логическое значение:798799```python800>>> class Color(Flag):801... BLACK = 0802... RED = auto()803... BLUE = auto()804... GREEN = auto()805...806>>> Color.BLACK807<Color.BLACK: 0>808>>> bool(Color.BLACK)809False810```811812Члены [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) также можно перебирать:813814```python815>>> purple = Color.RED | Color.BLUE816>>> list(purple)817[<Color.RED: 1>, <Color.BLUE: 2>]818```819820Добавлено в версии 3.12.821822> **Примечание**823>824> Для большинства нового кода настоятельно рекомендуются [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) и [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag), поскольку [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) и [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) нарушают некоторые семантические обещания перечисления (будучи сравнимыми с целыми числами, а значит, по транзитивности, и с другими не связанными перечислениями). [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) и [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) следует использовать только в тех случаях, когда [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) и [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) не подходят; например, при замене целочисленных констант на перечисления или для взаимодействия с другими системами.825826### Другие827828Хотя [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) является частью модуля [`enum`](https://python-all.ru/3.12/library/enum.html#module-enum), его очень просто реализовать самостоятельно:829830```python831class IntEnum(int, Enum):832 pass833```834835Это показывает, как можно определить подобные производные перечисления; например, `FloatEnum`, в который примешивается [`float`](https://python-all.ru/3.12/library/functions.html#float) вместо [`int`](https://python-all.ru/3.12/library/functions.html#int).836837Некоторые правила:8388391. При создании подкласса [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) типы-примеси должны располагаться перед самим [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) в последовательности базовых классов, как в [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) примере выше.8402. Примешиваемые типы должны быть наследуемыми. Например, [`bool`](https://python-all.ru/3.12/library/functions.html#bool) и [`range`](https://python-all.ru/3.12/library/stdtypes.html#range) не являются наследуемыми и приведут к ошибке во время создания Enum, если используются в качестве примешиваемого типа.8413. Хотя [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) может содержать члены любого типа, после примешивания дополнительного типа все члены должны иметь значения этого типа, например [`int`](https://python-all.ru/3.12/library/functions.html#int) выше. Это ограничение не распространяется на примеси, которые только добавляют методы и не задают другой тип.8424. При примешивании другого типа данных атрибут [`value`](https://python-all.ru/3.12/library/enum.html#enum.Enum.value) *отличается* от самого члена перечисления, хотя он эквивалентен и будет равным при сравнении.8435. `data type` – это примесь (mixin), которая определяет [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), или [`dataclass`](https://python-all.ru/3.12/library/dataclasses.html#dataclasses.dataclass)8446. Форматирование в стиле %: `%s` и `%r` вызывают [`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__) и [`__repr__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__repr__) класса [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) соответственно; другие коды (например, `%i` или `%h` для IntEnum) обрабатывают член перечисления как его примешанный тип.8457. [Форматированные строковые литералы](https://python-all.ru/3.12/reference/lexical_analysis.html#f-strings), [`str.format()`](https://python-all.ru/3.12/library/stdtypes.html#str.format) и [`format()`](https://python-all.ru/3.12/library/functions.html#format) будут использовать метод [`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__) перечисления.846847> **Примечание**848>849> Поскольку [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum), [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) и [`StrEnum`](https://python-all.ru/3.12/library/enum.html#enum.StrEnum) предназначены для замены существующих констант «на лету», их метод [`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__) был переопределён на метод [`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__) их типа данных.850851## Когда использовать [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) или [`__init__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__init__)852853[`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) следует использовать всякий раз, когда требуется настроить фактическое значение члена [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum). Любые другие изменения можно вносить как в [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), так и в [`__init__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__init__), при этом предпочтение отдаётся [`__init__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__init__).854855Например, если требуется передать конструктору несколько элементов, но только один из них должен быть значением:856857```python858>>> class Coordinate(bytes, Enum):859... """860... Согласование с двоичными кодами, которые можно индексировать по целочисленному коду.861... """862... def __new__(cls, value, label, unit):863... obj = bytes.__new__(cls, [value])864... obj._value_ = value865... obj.label = label866... obj.unit = unit867... return obj868... PX = (0, 'P.X', 'km')869... PY = (1, 'P.Y', 'km')870... VX = (2, 'V.X', 'km/s')871... VY = (3, 'V.Y', 'km/s')872...873874>>> print(Coordinate['PY'])875Coordinate.PY876877>>> print(Coordinate(3))878Coordinate.VY879```880881> **Предупреждение**882>883> *Не вызывайте* `super().__new__()`, поскольку будет найден `__new__`, предназначенный только для поиска; вместо этого используйте непосредственно тип данных.884885### Тонкие моменты886887#### Поддерживаемые `__dunder__` имена888889[`__members__`](https://python-all.ru/3.12/library/enum.html#enum.EnumType.__members__) представляет собой упорядоченное отображение только для чтения элементов `member_name`:`member`. Оно доступно только на уровне класса.890891[`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), если указан, должен создавать и возвращать элементы перечисления; также рекомендуется соответствующим образом установить [`_value_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._value_) элемента. После создания всех элементов он больше не используется.892893#### Поддерживаемые `_sunder_` имена894895- [`_name_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._name_) – имя члена896- [`_value_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._value_) – значение элемента; может быть установлено / изменено в `__new__`897- [`_missing_()`](https://python-all.ru/3.12/library/enum.html#enum.Enum._missing_) – функция поиска, используемая, когда значение не найдено; может быть переопределена898- [`_ignore_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._ignore_) – список имён, либо в виде [`list`](https://python-all.ru/3.12/library/stdtypes.html#list), либо [`str`](https://python-all.ru/3.12/library/stdtypes.html#str), которые не будут преобразованы в элементы и будут удалены из итогового класса899- [`_order_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._order_) – используется в коде для Python 2/3 для обеспечения согласованности порядка элементов (атрибут класса, удаляется при создании класса)900- [`_generate_next_value_()`](https://python-all.ru/3.12/library/enum.html#enum.Enum._generate_next_value_) – используется [функциональным API](https://python-all.ru/3.12/howto/enum.html#functional-api) и [`auto`](https://python-all.ru/3.12/library/enum.html#enum.auto) для получения подходящего значения для элемента перечисления; может быть переопределён901902> **Примечание**903>904> Для стандартных классов [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) следующее выбираемое значение равно последнему увиденному значению, увеличенному на единицу.905>906> Для классов [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) следующее выбираемое значение будет следующей по величине степенью двойки, независимо от последнего увиденного значения.907908Добавлено в версии 3.6: `_missing_`, `_order_`, `_generate_next_value_`909910Добавлено в версии 3.7: `_ignore_`911912Чтобы помочь синхронизировать код Python 2 и Python 3, можно указать атрибут [`_order_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._order_). Он будет проверен на соответствие фактическому порядку перечисления, и если они не совпадают, будет вызвана ошибка:913914```python915>>> class Color(Enum):916... _order_ = 'RED GREEN BLUE'917... RED = 1918... BLUE = 3919... GREEN = 2920...921Traceback (most recent call last):922...923TypeError: member order does not match _order_:924 ['RED', 'BLUE', 'GREEN']925 ['RED', 'GREEN', 'BLUE']926```927928> **Примечание**929>930> В коде Python 2 атрибут [`_order_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._order_) необходим, поскольку порядок определения теряется до того, как его можно зафиксировать.931932#### \_Private\_\_names933934[Приватные имена](https://python-all.ru/3.12/reference/expressions.html#private-name-mangling) не преобразуются в члены перечисления, а остаются обычными атрибутами.935936Изменено в версии 3.11.937938#### Тип члена `Enum`939940Члены перечисления являются экземплярами своего класса перечисления и обычно доступны как `EnumClass.member`. В определённых ситуациях, например, при написании пользовательского поведения перечисления, возможность прямого обращения одного члена к другому бывает полезна и поддерживается; однако во избежание конфликтов имён между именами членов и атрибутами/методами из примешанных классов настоятельно рекомендуется использовать имена в верхнем регистре.941942Изменено в версии 3.5.943944#### Создание членов, примешанных к другим типам данных945946При наследовании от других типов данных, таких как [`int`](https://python-all.ru/3.12/library/functions.html#int) или [`str`](https://python-all.ru/3.12/library/stdtypes.html#str), в сочетании с [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum), все значения после `=` передаются конструктору этого типа данных. Например:947948```python949>>> class MyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer950... example = '11', 16 # итак, x='11' и base=16951...952>>> MyEnum.example.value # и hex(11) – это...95317954```955956#### Логическое значение классов и членов `Enum`957958Классы перечислений, в которые примешаны не-[`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) типы (например, [`int`](https://python-all.ru/3.12/library/functions.html#int), [`str`](https://python-all.ru/3.12/library/stdtypes.html#str) и т.д.), вычисляются по правилам примешанного типа; в противном случае все члены вычисляются как [`True`](https://python-all.ru/3.12/library/constants.html#True). Чтобы логическое значение вашего перечисления зависело от значения члена, добавьте в свой класс следующее:959960```python961def __bool__(self):962 return bool(self.value)963```964965Простые классы [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) всегда вычисляются как [`True`](https://python-all.ru/3.12/library/constants.html#True).966967#### `Enum` классы с методами968969Если добавить в подкласс перечисления дополнительные методы, например, как в классе [Planet](https://python-all.ru/3.12/howto/enum.html#planet) ниже, эти методы появятся в [`dir()`](https://python-all.ru/3.12/library/functions.html#dir) элемента, но не класса:970971```python972>>> dir(Planet)973['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']974>>> dir(Planet.EARTH)975['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']976```977978#### Объединение элементов `Flag`979980Итерация по комбинации элементов [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) вернёт только те элементы, которые состоят из одного бита:981982```python983>>> class Color(Flag):984... RED = auto()985... GREEN = auto()986... BLUE = auto()987... MAGENTA = RED | BLUE988... YELLOW = RED | GREEN989... CYAN = GREEN | BLUE990...991>>> Color(3) # именованная комбинация992<Color.YELLOW: 3>993>>> Color(7) # не именованная комбинация994<Color.RED|GREEN|BLUE: 7>995```996997#### `Flag` и `IntFlag` мелочи998999Используя следующий фрагмент для примеров:10001001```python1002>>> class Color(IntFlag):1003... BLACK = 01004... RED = 11005... GREEN = 21006... BLUE = 41007... PURPLE = RED | BLUE1008... WHITE = RED | GREEN | BLUE1009...1010```10111012верно следующее:10131014- однобитовые флаги являются каноническими1015- многобитовые и нулевые флаги являются псевдонимами1016- при итерации возвращаются только канонические флаги:10171018 ```python1019 >>> list(Color.WHITE)1020 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]1021 ```1022- отрицание флага или набора флагов возвращает новый флаг/набор флагов с соответствующим положительным целым значением:10231024 ```python1025 >>> Color.BLUE1026 <Color.BLUE: 4>10271028 >>> ~Color.BLUE1029 <Color.RED|GREEN: 3>1030 ```1031- имена псевдофлагов формируются из имён их элементов:10321033 ```python1034 >>> (Color.RED | Color.GREEN).name1035 'RED|GREEN'10361037 >>> class Perm(IntFlag):1038 ... R = 41039 ... W = 21040 ... X = 11041 ...1042 >>> (Perm.R & Perm.W).name is None # фактически Perm(0)1043 True1044 ```1045- многобитовые флаги, также известные как псевдонимы, могут возвращаться в результате операций:10461047 ```python1048 >>> Color.RED | Color.BLUE1049 <Color.PURPLE: 5>10501051 >>> Color(7) # или Color(-1)1052 <Color.WHITE: 7>10531054 >>> Color(0)1055 <Color.BLACK: 0>1056 ```1057- проверка членства/включения: нулевые флаги всегда считаются содержащимися:10581059 ```python1060 >>> Color.BLACK in Color.WHITE1061 True1062 ```10631064 в противном случае True возвращается, только если все биты одного флага содержатся в другом флаге:10651066 ```python1067 >>> Color.PURPLE in Color.WHITE1068 True10691070 >>> Color.GREEN in Color.PURPLE1071 False1072 ```10731074Появился новый механизм границ, который управляет обработкой выходящих за пределы / недопустимых битов: `STRICT`, `CONFORM`, `EJECT` и `KEEP`:10751076- STRICT –\> вызывает исключение при получении недопустимых значений1077- CONFORM –\> отбрасывает любые недопустимые биты1078- EJECT –\> теряет статус Flag и становится обычным int с заданным значением1079- KEEP –\> сохраняет дополнительные биты10801081 - сохраняет статус Flag и дополнительные биты1082 - дополнительные биты не отображаются при итерации1083 - дополнительные биты отображаются в repr() и str()10841085По умолчанию для Flag используется `STRICT`, по умолчанию для `IntFlag` – `EJECT`, а по умолчанию для `_convert_` – `KEEP` (см. `ssl.Options` для примера, когда требуется `KEEP`).10861087## Чем отличаются перечисления и флаги?10881089Перечисления имеют собственный метакласс, который влияет на многие аспекты как производных классов [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum), так и их экземпляров (элементов).10901091### Классы Enum10921093Метакласс [`EnumType`](https://python-all.ru/3.12/library/enum.html#enum.EnumType) отвечает за предоставление методов [`__contains__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__contains__), [`__dir__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__dir__), [`__iter__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__iter__) и других, которые позволяют выполнять с классом [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) действия, невозможные для обычного класса, например, `list(Color)` или `some_enum_var in Color`. [`EnumType`](https://python-all.ru/3.12/library/enum.html#enum.EnumType) отвечает за обеспечение корректности различных других методов итогового класса [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) (таких как [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), [`__getnewargs__()`](https://python-all.ru/3.12/library/pickle.html#object.__getnewargs__), [`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__) и [`__repr__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__repr__)).10941095### Классы Flag10961097Флаги имеют расширенное представление о псевдонимах: чтобы быть каноническим, значение флага должно быть степенью двойки и не быть дублирующимся именем. Таким образом, в дополнение к определению псевдонима [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum), флаг без значения (также известный как `0`) или с более чем одним значением, являющимся степенью двойки (например, `3`), считается псевдонимом.10981099### Элементы Enum (также известные как экземпляры)11001101Самое интересное в членах перечисления – то, что они синглтоны. [`EnumType`](https://python-all.ru/3.12/library/enum.html#enum.EnumType) создаёт их все при создании самого класса перечисления, а затем подставляет собственный [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), чтобы гарантировать, что новые экземпляры никогда не будут созданы, возвращая только существующие.11021103### Члены флага11041105Члены флага можно перебирать так же, как и класс [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag), и будут возвращены только канонические члены. Например:11061107```python1108>>> list(Color)1109[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]1110```11111112(Обратите внимание, что `BLACK`, `PURPLE` и `WHITE` не отображаются.)11131114Инвертирование члена флага возвращает соответствующее положительное значение, а не отрицательное. Например:11151116```python1117>>> ~Color.RED1118<Color.GREEN|BLUE: 6>1119```11201121Члены флага имеют длину, соответствующую количеству содержащихся в них степеней двойки. Например:11221123```python1124>>> len(Color.PURPLE)112521126```11271128## Сборник рецептов Enum11291130Хотя [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum), [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum), [`StrEnum`](https://python-all.ru/3.12/library/enum.html#enum.StrEnum), [`Flag`](https://python-all.ru/3.12/library/enum.html#enum.Flag) и [`IntFlag`](https://python-all.ru/3.12/library/enum.html#enum.IntFlag) призваны покрывать большинство сценариев использования, они не могут охватить их все. Здесь приведены рецепты для некоторых различных типов перечислений, которые можно использовать напрямую или в качестве примеров для создания собственных.11311132### Пропуск значений11331134Во многих сценариях пользователю всё равно, каково фактическое значение перечисления. Существует несколько способов определить такой простой тип перечисления:11351136- использовать экземпляры [`auto`](https://python-all.ru/3.12/library/enum.html#enum.auto) в качестве значения1137- использовать экземпляры [`object`](https://python-all.ru/3.12/library/functions.html#object) в качестве значения1138- использовать описательную строку в качестве значения1139- использовать кортеж в качестве значения и собственный [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) для замены кортежа на значение [`int`](https://python-all.ru/3.12/library/functions.html#int)11401141Использование любого из этих методов показывает пользователю, что эти значения не важны, а также позволяет добавлять, удалять или переупорядочивать члены без необходимости перенумерации остальных членов.11421143#### Использование [`auto`](https://python-all.ru/3.12/library/enum.html#enum.auto)11441145Использование [`auto`](https://python-all.ru/3.12/library/enum.html#enum.auto) будет выглядеть так:11461147```python1148>>> class Color(Enum):1149... RED = auto()1150... BLUE = auto()1151... GREEN = auto()1152...1153>>> Color.GREEN1154<Color.GREEN: 3>1155```11561157#### Использование [`object`](https://python-all.ru/3.12/library/functions.html#object)11581159Использование [`object`](https://python-all.ru/3.12/library/functions.html#object) будет выглядеть так:11601161```python1162>>> class Color(Enum):1163... RED = object()1164... GREEN = object()1165... BLUE = object()1166...1167>>> Color.GREEN1168<Color.GREEN: <object object at 0x...>>1169```11701171Это также хороший пример того, зачем может понадобиться написать собственный [`__repr__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__repr__):11721173```python1174>>> class Color(Enum):1175... RED = object()1176... GREEN = object()1177... BLUE = object()1178... def __repr__(self):1179... return "<%s.%s>" % (self.__class__.__name__, self._name_)1180...1181>>> Color.GREEN1182<Color.GREEN>1183```11841185#### Использование описательной строки11861187Использование строки в качестве значения будет выглядеть так:11881189```python1190>>> class Color(Enum):1191... RED = 'stop'1192... GREEN = 'go'1193... BLUE = 'too fast!'1194...1195>>> Color.GREEN1196<Color.GREEN: 'go'>1197```11981199#### Использование собственного [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__)12001201Использование автонумерующего [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) будет выглядеть так:12021203```python1204>>> class AutoNumber(Enum):1205... def __new__(cls):1206... value = len(cls.__members__) + 11207... obj = object.__new__(cls)1208... obj._value_ = value1209... return obj1210...1211>>> class Color(AutoNumber):1212... RED = ()1213... GREEN = ()1214... BLUE = ()1215...1216>>> Color.GREEN1217<Color.GREEN: 2>1218```12191220Чтобы сделать более универсальный `AutoNumber`, добавьте `*args` в сигнатуру:12211222```python1223>>> class AutoNumber(Enum):1224... def __new__(cls, *args): # это единственное изменение по сравнению с предыдущим1225... value = len(cls.__members__) + 11226... obj = object.__new__(cls)1227... obj._value_ = value1228... return obj1229...1230```12311232Затем при наследовании от `AutoNumber` можно написать собственный `__init__` для обработки любых дополнительных аргументов:12331234```python1235>>> class Swatch(AutoNumber):1236... def __init__(self, pantone='unknown'):1237... self.pantone = pantone1238... AUBURN = '3497'1239... SEA_GREEN = '1246'1240... BLEACHED_CORAL = () # Новый цвет, кода Pantone пока нет!1241...1242>>> Swatch.SEA_GREEN1243<Swatch.SEA_GREEN: 2>1244>>> Swatch.SEA_GREEN.pantone1245'1246'1246>>> Swatch.BLEACHED_CORAL.pantone1247'unknown'1248```12491250> **Примечание**1251>1252> Метод [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__), если он определён, используется во время создания членов Enum; затем он заменяется методом [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) класса Enum, который используется после создания класса для поиска существующих членов.12531254> **Предупреждение**1255>1256> *Не вызывайте* `super().__new__()`, так как будет найден `__new__` только для поиска; вместо этого используйте непосредственно тип данных – например:1257>1258> ```python1259> obj = int.__new__(cls, value)1260> ```12611262### OrderedEnum12631264Упорядоченное перечисление, которое не основано на [`IntEnum`](https://python-all.ru/3.12/library/enum.html#enum.IntEnum) и поэтому сохраняет обычные инварианты [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) (например, несравнимость с другими перечислениями):12651266```python1267>>> class OrderedEnum(Enum):1268... def __ge__(self, other):1269... if self.__class__ is other.__class__:1270... return self.value >= other.value1271... return NotImplemented1272... def __gt__(self, other):1273... if self.__class__ is other.__class__:1274... return self.value > other.value1275... return NotImplemented1276... def __le__(self, other):1277... if self.__class__ is other.__class__:1278... return self.value <= other.value1279... return NotImplemented1280... def __lt__(self, other):1281... if self.__class__ is other.__class__:1282... return self.value < other.value1283... return NotImplemented1284...1285>>> class Grade(OrderedEnum):1286... A = 51287... B = 41288... C = 31289... D = 21290... F = 11291...1292>>> Grade.C < Grade.A1293True1294```12951296### DuplicateFreeEnum12971298Вызывает ошибку, если обнаружено повторяющееся значение члена, вместо создания псевдонима:12991300```python1301>>> class DuplicateFreeEnum(Enum):1302... def __init__(self, *args):1303... cls = self.__class__1304... if any(self.value == e.value for e in cls):1305... a = self.name1306... e = cls(self.value).name1307... raise ValueError(1308... "aliases not allowed in DuplicateFreeEnum: %r --> %r"1309... % (a, e))1310...1311>>> class Color(DuplicateFreeEnum):1312... RED = 11313... GREEN = 21314... BLUE = 31315... GRENE = 21316...1317Traceback (most recent call last):1318 ...1319ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN'1320```13211322> **Примечание**1323>1324> Это полезный пример подкласса Enum для добавления или изменения другого поведения, а также запрета псевдонимов. Если требуется только запрет псевдонимов, вместо этого можно использовать декоратор [`unique()`](https://python-all.ru/3.12/library/enum.html#enum.unique).13251326### Planet13271328Если определены [`__new__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__new__) или [`__init__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__init__), значение элемента перечисления будет передаваться этим методам:13291330```python1331>>> class Planet(Enum):1332... MERCURY = (3.303e+23, 2.4397e6)1333... VENUS = (4.869e+24, 6.0518e6)1334... EARTH = (5.976e+24, 6.37814e6)1335... MARS = (6.421e+23, 3.3972e6)1336... JUPITER = (1.9e+27, 7.1492e7)1337... SATURN = (5.688e+26, 6.0268e7)1338... URANUS = (8.686e+25, 2.5559e7)1339... NEPTUNE = (1.024e+26, 2.4746e7)1340... def __init__(self, mass, radius):1341... self.mass = mass # в килограммах1342... self.radius = radius # в метрах1343... @property1344... def surface_gravity(self):1345... # гравитационная постоянная (м3 кг-1 с-2)1346... G = 6.67300E-111347... return G * self.mass / (self.radius * self.radius)1348...1349>>> Planet.EARTH.value1350(5.976e+24, 6378140.0)1351>>> Planet.EARTH.surface_gravity13529.8026527433371291353```13541355### TimePeriod13561357Пример использования атрибута [`_ignore_`](https://python-all.ru/3.12/library/enum.html#enum.Enum._ignore_):13581359```python1360>>> from datetime import timedelta1361>>> class Period(timedelta, Enum):1362... "different lengths of time"1363... _ignore_ = 'Period i'1364... Period = vars()1365... for i in range(367):1366... Period['day_%d' % i] = i1367...1368>>> list(Period)[:2]1369[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]1370>>> list(Period)[-2:]1371[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]1372```13731374## Создание подкласса EnumType13751376Хотя большинство потребностей в перечислениях можно удовлетворить, настраивая подклассы [`Enum`](https://python-all.ru/3.12/library/enum.html#enum.Enum) с помощью декораторов классов или пользовательских функций, [`EnumType`](https://python-all.ru/3.12/library/enum.html#enum.EnumType) можно субклассировать для получения другого опыта работы с Enum.1377