Класс Enum в Python [2] это набор символических имен, привязанных к уникальным значениям. Это подобно глобальным переменным, но также предоставляются repr(), группирование, type-safety и другие удобные фичи.
Значения перечисления больше всего подходят для случая, когда переменная может иметь ограниченный набор выбираемых значений. Например, это могут быть дни недели:
>>> from enum import Enum >>> classColor(Enum): ... RED = 1 ... GREEN = 2 ... BLUE = 3
Как вы можете видеть, создание перечисления Enum это просто написание класса, который наследуется от самого Enum.
Замечание по поводу использования прописных/строчных букв: поскольку перечисления используются для представления констант, то воизбежание проблем конфликта с именами переменных и методов настоятельно рекомендуется ИСПОЛЬЗОВАТЬ ВЕРХНИЙ РЕГИСТР для имен значений перечисления. В примерах как раз будет использоваться такой стиль именования.
В зависимости от характера перечисления значение его члена может быть или не быть важным, но в любом случае это значение может быть использовано для получения соответствующего члена:
>>> Weekday(3)
< Weekday.WEDNESDAY: 3>
Как вы можете видеть, функция repr() элемента перечисления покажет имя enum, имя члена enum и значение члена enum. Функция str() элемента перечисления покажет только имя enum и имя члена enum:
>>> print(Weekday.THURSDAY)
Weekday.THURSDAY
Функция type() для элемента перечисления покажет, какому enum он принадлежит:
Элементы Enum имеют атрибут name, который просто содержит их имя:
>>> print(Weekday.TUESDAY.name)
TUESDAY
Подобным образом есть атрибут value, который содержит значение элемента Enum:
>>> Weekday.WEDNESDAY.value 3
В отличие от многих языков программирования, которые обрабатывают перечисления чисто только как пары имя/значение, Python Enum может иметь добавленное поведение. Например, datetime.date содержит 2 метода для возврата недели: weekday() и isoweekday(). Они различаются тем, что один считает от 0 до 6, а другой от 1 до 7. Вместо того, чтобы отслеживать это самостоятельно, мы может добавить метод в перечисление Weekday, чтобы извлечь день из экземпляра date, и возвратить соответствующий элемент перечисления:
Теперь мы можем определить, какой сегодня день! Смотрите:
>>> from datetime import date
Weekday.from_date(date.today())
< Weekday.TUESDAY: 2>
Конечно, если вы попробуете это в какой-то другой день, то выведенное значение будет другим.
Перечисление Weekday хорошо подойдет, если нашей переменной нужно значение только одного дня, но что если нам понадобится несколько? Возможно, что мы пишем функцию планирования дел в течение недели, и не хотим использовать список – тогда мы могли бы использовать другой тип Enum:
Пример функции, которая покажет, что запланировано на указанный день:
>>> defshow_chores(chores, day): ... for chore, days in chores.items(): ... if day in days: ... print(chore) >>> show_chores(chores_for_ethan, Weekday.SATURDAY)
Ответить на вопросы
В тех случаях, когда реальные значения элементов Flag не имеют значения, можно сэкономить усилия, используя auto() для значений элементов:
[Программный доступ к элементам перечисления и их атрибутам]
Иногда полезно программно обращаться к члена в перечислениях (например это ситуации, где Color.RED не то, что работает, потому что точный цвет неизвестен во время написания программы). Enum позволяет осуществлять такой доступ:
Однако элементы перечисления могут иметь имена, связанные с другим именем. Если дать элементам A и B одинаковое значение, и A был определен первым, то B становится псевдонимом (alias) для элемента A. Выборка по имени A возвратит элемент A. Выборка по имени B также возвратит элемент A:
Замечание: попытка создать элемент с таким же именем, что и уже определенный атрибут (другой элемент, метод, и т. д.), или попытка создать атрибут с таким же именем, как у элемента - все это не допускается.
По умолчанию перечисления позволяют несколько имен как псевдонимы одного и того же значения. Но когда такое поведение нежелательно, вы можете использовать декоратор unique():
>>> from enum import Enum, unique @unique classMistake(Enum): ... ONE = 1 ... TWO = 2 ... THREE = 3 ... FOUR = 3 ...
Traceback (most recent call last):
...
ValueError: duplicate values found in < enum 'Mistake'>: FOUR -> THREE
[Использование автоматических значений]
Если точное значение не важно, то вы можете использовать auto:
>>> from enum import Enum, auto classColor(Enum): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ...
[member.value for member in Color]
[1, 2, 3]
Значения, выбранные функцией _generate_next_value_(), которые могут быть переназначены:
>>> classAutoName(Enum): ... @staticmethod ... def_generate_next_value_(name, start, count, last_values): ... return name ... >>> classOrdinal(AutoName): ... NORTH = auto() ... SOUTH = auto() ... EAST = auto() ... WEST = auto() ... >>> [member.value for member in Ordinal]
['NORTH', 'SOUTH', 'EAST', 'WEST']
Обратите внимание, что метод _generate_next_value_() должен быть определен перед любыми элементами.
[Итерация]
Итерация по элементам enum не предоставляет псевдонимы:
Обратите внимание, что не показаны псевдонимы Shape.ALIAS_FOR_SQUARE и Weekday.WEEKEND.
Специальный атрибут __members__ это упорядоченное read-only отображение имен на элементы перечисления. Он включает все имена, определенные в перечислении, в том числе и псевдонимы:
>>> 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__ может использовать для детализированного программного доступа к элементам перечисления. Например, вот так можно найти все псевдонимы:
>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']
Обратите внимание, что псевдонимы для флагов включают значения с несколькими установленными флагами, такие как 3, и без установленных флагов, т. е. 0.
[Сравнения]
Элементы перечисления можно сравнивать по идентичности:
>>> Color.RED is Color.RED True >>> Color.RED is Color.BLUE False >>> Color.RED isnot Color.BLUE True
Упорядоченные сравнения между значениями перечислений не поддерживаются. Элементы перечисления не являются целыми числами (однако см. далее IntEnum):
>>> Color.RED < Color.BLUE
Traceback (most recent call last):
File "< stdin>", line 1, in < module>
TypeError: '< 'not supported between instances of 'Color'and'Color'
Сравнения на равенство определяются следующим образом:
Сравнения с чем-то, не относящимся к значениям перечисления, всегда покажет не эквивалентность (и опять-таки, IntEnum было разработано с полностью другим поведением, о чем дальше):
>>> Color.BLUE == 2 False
Предупреждение: можно перезагрузить модули – если перезагруженный модуль содержит перечисления, то они будут созданы заново, и новые элементы могут не быть идентичны/эквивалентны оригинальным элементам.
[Разрешенные элементы и атрибуты перечислений]
Большинство примеров выше используют целые числа для значений перечисления. Использование целых чисел это краткий и удобный способ (и он предоставляется по умолчанию в Functional API), но он не строго соблюдается. В подавляющем большинстве случаев использования не важно, какое фактическое значение перечисления. Но если значение важно, то перечисления могут иметь произвольные значения.
Перечисления являются в Python классами, и они могут иметь методы, как обычно. Если у нас есть такое перечисление:
Правила для дозволенного следующие: имена, которые начинаются и заканчиваются одним символом подчеркивания, зарезервированы перечислением и не могут использоваться; все другие атрибуты, определенные в перечислении, станут членами этого перечисления, за исключением специальных методов (__str__(), __add__(), и т. п.), дескрипторов (методы тоже являются дескрипторами), и имен переменных, перечисленных в _ignore_.
Замечание: если ваше перечисление определяет __new__() и/или __init__(), то любое значение (значения) указанные для элементов enum будут переданы в эти методы. В качестве примера см. класс Planet.
Обратите внимание, что метод __new__(), если определен, используется во время создания элементов Enum; затем он заменяется новым Enum-методом __new__(), который используется после создания класса для поиска существующих элементов. Для дополнительной информации см. "Когда использовать __new__(), а когда __init__()".
[Ограничение создания субклассов Enum]
Новый класс Enum должен иметь один базовый перечисления, до одного конкретного типа данных, и столько смешанных, основанных на объектах классов (object-based mixin classes) сколько необходимо. Порядок этих базовых классов:
Разрешение субклассов перечислений, которые определяют элементы, приведет к нарушению некоторых важных инвариантов типов и экземпляров. С другой стороны, имеет смысл разрешить совместное использование некоторого общего поведения группой перечислений (см. пример в OrderedEnum).
[Поддержка dataclass]
При наследовании из dataclass функция __repr__() опускает наследуемое имя класса. Например:
Используйте аргумент repr=False для dataclass(), чтобы использовать стандартную repr().
Изменено в версии 3.12: в области значений отображаются только поля dataclass, а не имя dataclass.
Замечание: добавление декоратора dataclass() для Enum и его субклассов не поддерживается. Это не вызовет никаких ошибок, но приведет к очень странным результатам runtime, например, если элементы равны друг другу:
>>> @dataclass # не делайте так, это бессмысленно ... classColor(Enum): ... RED = 1
... BLUE = 2 ... >>> Color.RED is Color.BLUE False >>> Color.RED == Color.BLUE # проблема: здесь не должно быть равенства True
>>> from test.test_enum import Fruit >>> from pickle import dumps, loads >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO)) True
Применяются обычные ограничения для pickling: picklable-перечисления должны быть определены на верхнем уровне модуля, поскольку unpickling требует, чтобы они могли быть импортированы из этого модуля.
Замечание: с pickle-протоколом версии 4 есть возможность просто делать pickle перечислений, вложенных другие классы.
Можно изменить, как элементы enum будут pickled/unpickled, путем определения __reduce_ex__() в классе перечисления. Метод по умолчанию by-value (по значению), однако перечисления со сложными значениями могут захотеть вариант by-name (по имени):
Семантика этого API напоминает именованный кортеж (namedtuple). Первым аргументом вызова Enum является имя перечисления.
Второй аргумент это источник (source) имен элементов перечисления. Это может быть строка имен, где имена разделены пробелами, последовательность имен, последовательность 2-tuple с парами key/value, или mapping (отображение, т. е. словарь) имен на значения. Последние две опции позволяют назначать произвольные значения для перечислений; другие автоматически присваивают возрастающие целые числа, начиная с 1 (используйте параметр start для указания другого начального значения). Будет возвращен новый класс, унаследованный от Enum. Другими словами, показанное выше присваивание Animal эквивалентно следующему:
>>> classAnimal(Enum): ... ANT = 1 ... BEE = 2 ... CAT = 3 ... DOG = 4 ...
Причина, по которой по умолчанию для начального значения используется 1, а не 0, состоит в том, что 0 это False в двоичном контексте, но по умолчанию все элементы перечисления оцениваются как True.
Pickling перечислений, созданных функциональным API, может быть сложной, поскольку используются детали реализации фрейма стека, чтобы попытаться выяснить, в каком модуле создается перечисление (например, это потерпит неудачу, если вы используете служебную функцию в отдельном модуле, и также может не работать на IronPython или Jython). Решение состоит в явном указании имени модуля следующим образом:
Предупреждение: если модуль не предоставлен, то Enum не может определить, что это такое, новые элементы Enum будут unpicklable; чтобы держать ошибки ближе к источнику, pickling будет запрещен.
Новый pickle-протокол 4, в некоторых обстоятельствах, полагается на установку __qualname__ в месте расположения, где pickle сможет найти класс. Например, если класс был сделан доступным в классе SomeData в глобальной области видимости:
• module: имя модуля, где можно найти новый класс enum.
• qualname: где в модуле можно найти новый класс enum.
• type: тип для смешивания с новым классом enum.
• start: число, от которого начнется отсчет при передаче только имен.
Изменено в версии 3.5: был добавлен параметр start.
[Унаследованные от перечислений классы]
IntEnum. Первый предоставляемый вариант Enum также является подклассом от int. Элементы IntEnum можно сравнивать с целыми числами; по расширению, целочисленные перечисления различных типов можно сравнивать друг с другом:
Однако, их все еще нельзя сравнивать со стандартными перечислениями Enum:
>>> classShape(IntEnum): ... CIRCLE = 1 ... SQUARE = 2 ... >>> classColor(Enum): ... RED = 1 ... GREEN = 2 ... >>> Shape.CIRCLE == Color.RED False
Значения IntEnum ведут себя как целые числа и в других случаях, что вполне ожидаемо:
>>> int(Shape.CIRCLE) 1 >>> ['a', 'b', 'c'][Shape.CIRCLE] 'b' >>> [i for i inrange(Shape.SQUARE)]
[0, 1]
StrEnum. Второй предоставляемый вариант Enum также является подклассом от str. Элементы StrEnum можно сравнивать со строками; по расширению, перечисления строк различных типов можно сравнивать друг с другом.
Добавлено в версии 3.11.
IntFlag. Следующий предоставляемый вариант Enum, IntFlag, также основан на int. Отличие элементов IntFlag в том, что их можно комбинировать битовыми операторами (&, |, ^, ~), и результат все еще будет элементом IntFlag, если это возможно. Наподобие IntEnum, элементы IntFlag также являются целыми числами и могут использоваться везде, где используется int.
Замечание: любая операция над элементом IntFlag, кроме перечисленных выше битовых операций, приводит потере членства в IntFlag. Битовые операции, которые приводят к недопустимым значениям IntFlag, теряют членство IntFlag. Подробности см. в описании FlagBoundary [2].
Добавлено в версии 3.6.
Изменено в версии 3.11.
Пример класса IntFlag:
>>> from enum import IntFlag >>> classPerm(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
Замечание: именованные комбинации считаются псевдонимами. Псевдонимы не показываются в итерации, однако могут быть возвращены обращениями по значению (by-value lookup).
Изменено в версии 3.11.
Другое важное отличие IntFlag и Enum в том, что если ни один флаг элемента не установлен (значение 0), то его двоичная оценка равна False:
Элементы IntFlag также могут быть перечислены следующим образом:
>>> list(RW)
[< Perm.R: 4>, < Perm.W: 2>]
Добавлено в версии 3.11.
Flag. Последняя вариация это Flag. Так же, как и IntFlag, элементы Flag могут комбинироваться битовыми операторами (&, |, ^, ~). В отличие от IntFlag, они не могут быть ни комбинированы, ни сравниваемыми, с любым другим перечислением Flag, а также не могут комбинироваться и сравниваться с int. Хотя можно напрямую указывать значения, рекомендуется использовать auto в качестве значений, и позволить классу Flag выбрать подходящее значение.
Добавлено в версии 3.6.
Наподобие IntFlag, если комбинация элементов Flag приведет к тому, что ни один флаг н установлен, то его двоичная оценка будет False:
>>> from enum import Flag, auto >>> classColor(Flag): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> Color.RED & Color.GREEN
< Color: 0> >>> bool(Color.RED & Color.GREEN) False
Отдельные флаги должны иметь значения степеней двойки (1, 2, 4, 8, ...), в то время как их комбинации не будут значения степеней двойки:
>>> classColor(Flag): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... WHITE = RED | BLUE | GREEN ... >>> Color.WHITE
< Color.WHITE: 7>
Присвоение имени состояния "ни один флаг не установлен" не меняет его логического значения:
>>> classColor(Flag): ... BLACK = 0 ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> Color.BLACK
< Color.BLACK: 0> >>> bool(Color.BLACK)False
Замечание: для большинства нового кода строго рекомендуется использовать Enum и Flag, поскольку IntEnum и IntFlag нарушают некоторые семантические обещания перечисления (поскольку их можно сравнивать с целыми числами, и следовательно есть риск перехода к другим не связанным перечислениям). IntEnum и IntFlag должны использоваться только в случаях, где Enum и Flag не работают; например, когда целочисленные константы заменяются перечислениями, или для взаимодействия с другими системами.
Другое. Хотя класс IntEnum это часть модуля enum, его очень просто реализовать независимо:
classIntEnum(int, ReprEnum): # или Enum вместо ReprEnumpass
Это демонстрирует, как как можно определить подобные производные перечисления; например, FloatEnum, который смешивается в float вместо int.
Некоторые правила:
1. Когда выполняется наследование от Enum, mix-in типы должны появляться перед самим классом Enum в последовательности базовых классов, как в показанном выше примере IntEnum.
2. Mix-in типы должны быть способны к созданию производных классов (subclassable). Например, bool и range не являются subclassable, и будет выброшено исключение ошибки (throw error) при создании Enum, если они будут использоваться в качестве mix-in типа.
3. Хотя Enum может иметь элементы любого типа, после смешивания в дополнительный тип все члены должны иметь значения этого типа, например int как в примере выше. Это ограничение не применяется mix-in типам, в которых только добавляются методы, и не указывается другой тип.
4. При смешивании другого типа данных атрибут value не совпадает с самим элементом перечисления, хотя он эквивалентен и при сравнении окажется равным.
5. Тип данных (data type) это mixin, который определяет __new__() или dataclass.
6. Форматирование в %-стиле: %s и %r вызывают соответственно методы __str__() и __repr__() класса Enum; другие коды формата (такие как %i или %h для IntEnum) обрабатывают элемент enum как его mixed-in тип.
7. Строковые литералы форматирования str.format() и format() будут использовать enum-метод __str__().
Замечание: поскольку IntEnum, IntFlag и StrEnum предназначены для замены существующих констант, их метод __str__() был сброшен в их тип данных метода __str__().
[Когда использовать __new__(), а когда __init__()]
Метод __new__() должен использоваться всякий раз, когда вы хотите переделать под себя реальное значение элемента Enum. Любые другие модификации могут произойти либо в __new__(), либо в __init__(), предпочтительнее в __init__().
Например, если вы хотите передать несколько элементов в конструктор, но только один из них должен быть значением:
Предупреждение: не вызывайте super().__new__(), так как найдется lookup-only __new__; вместо этого используйте тип данных напрямую.
[Замечания о тонкостях]
Поддерживаемые имена __dunder__.
__members__ это упорядоченное read-only отображение элементов member_name:member. Это доступно только на классе.
__new__(), если указан, должен создать и возвратить элементы enum; также очень хорошей идеей будет установить _value_ элементов соответствующим образом. После создания всех элементов он больше не используется.
Поддерживаемые имена _sunder_. Назначение полей и методов:
_name_ – имя элемента.
_value_ – значение элемента; может быть установлено в __new__.
_missing_() – функция поиска, когда значение не найдено; этот метод может быть переопределен.
_ignore_ – список имен либо в виде списка, либо str, которые не будут преобразованы в элементы, и будут удалены их конечного класса.
_generate_next_value_() – используется для получения подходящего значения для элемента enum; этот метод может быть переопределен.
_add_alias_() – добавит новое имя как псевдоним к существующему элементу.
_add_value_alias_() – добавит новое значение как псевдоним существующего элемента. Для примера см. MultiValueEnum.
Замечание: для стандартных классов Enum следующее значение выбирается из самого большого видимого значения, инкрементированного на 1. Для классов Flag следующее значение это следующая самая большая степень двойки.
Изменено в версии 3.13: в предыдущих версиях вместо наибольшего значения использовалось последнее видимое значение.
Добавлено в версии 3.6: _missing_, _order_, _generate_next_value_.
Добавлено в версии 3.7: _ignore_.
Добавлено в версии 3.13: _add_alias_, _add_value_alias_.
Для синхронизации кода Python 2 / Python 3 может быть предоставлен атрибут _order_. Он будет проверен на фактически порядок перечисления и выбросит исключение ошибки, если они не совпадают:
>>> classColor(Enum): ... _order_ = 'RED GREEN BLUE' ... RED = 1 ... BLUE = 3 ... GREEN = 2 ...
Traceback (most recent call last):
...
TypeError: member order does notmatch _order_:
['RED', 'BLUE', 'GREEN']
['RED', 'GREEN', 'BLUE']
Замечание: в коде Python 2 атрибут _order_ необходим, поскольку порядок определения теряется, прежде чем его можно будет записать.
_Private__names. Приватные имена не преобразуются в элементы enum, однако остаются нормальными атрибутами.
Изменено в версии 3.11.
Тип элемента Enum. Элементы Enum являются экземплярами своего класса enum, и к ним нормально происходит обращение как к EnumClass.member. В определенных ситуациях, таких как написание пользовательского поведения enum, возможность доступа к одному элементу непосредственно их другого является полезной и поддерживается; однако во избежание конфликтов имен между именами элементов и атрибутами/методами из mixed-in классов, настоятельно рекомендуется использовать имена в верхнем регистре.
Изменено в версии 3.5.
Создание элементов, смешанных с другими типами данных. Когда выполняется наследование других типов данных, таких как int или str, вместе с Enum, все значения после = передаются конструктору этого типа данных. Например:
>>> classMyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer ... example = '11', 16# так x='11' и base=16 ... >>> MyEnum.example.value # и hex(11) это .. 17
Boolean-значение классов Enum и его элементов. Классы Enum, которые смешиваются с не-Enum типами (такими как int, str, и т. д.) вычисляются в соответствии с правилами смешанных тиров (mixed-in type rules); иначе все элементы вычисляются как True. Чтобы создать ваше собственное вычисление boolean перечисления, зависящее от значения элемента, добавьте в свой класс следующее:
def__bool__(self):
returnbool(self.value)
Простые классы Enum всегда вычисляются как True.
Классы Enum с методами. Если вы дадите своим enum-подклассам дополнительные методы, наподобие как в показанном далее классе Planet, то эти методы будут отображаться в dir() элемента, но не класса:
• отрицание флага или установка флага возвратит новый флаг / набор флагов с соответствующим положительным значением:
>>> Color.BLUE
< Color.BLUE: 4>
>>> ~Color.BLUE
< Color.RED|GREEN: 3>
• имена псевдофлагов строятся из имен их членов:
>>> (Color.RED | Color.GREEN).name 'RED|GREEN'
>>> classPerm(IntFlag): ... R = 4 ... W = 2 ... X = 1 ... >>> (Perm.R & Perm.W).name isNone# effectively Perm(0) True
• флаги с несколькими битами, также известные как псевдонимы, могут быть возвращены из операций:
>>> Color.RED | Color.BLUE
< Color.PURPLE: 5>
>>> Color(7) # или Color(-1)
< Color.WHITE: 7>
>>> Color(0)
< Color.BLACK: 0>
• проверка членства / включения: флаги с нулевым значением всегда считаются содержащимися:
>>> Color.BLACK in Color.WHITE True
Иначе только если все биты одного флага находятся в другом флаге, будет возвращено True:
>>> Color.PURPLE in Color.WHITE True
>>> Color.GREEN in Color.PURPLE False
Существует новый механизм границ, который управляет, как обрабатываются биты вне диапазона (out-of-range) / недопустимые (invalid): STRICT, CONFORM, EJECT и KEEP:
• STRICT –> выбрасывается исключение, когда имеются недопустимые значения.
• CONFORM –> отбрасываются любые недопустимые биты.
• EJECT –> теряется статус Flag и становится обычным int с имеющимся значением.
• KEEP –> сохранение лишних бит:
- сохраняется статус Flag и лишние биты. - лишние биты не показываются в итерации. - лишние биты не показываются в repr() и str().
Для Flag по умолчанию используется STRICT, для IntFlag используется умолчание EJECT, и умолчанием для _convert_ будет KEEP (см. ssl.Options [3] для примера, когда необходимо KEEP).
[Чем отличаются Enums и Flags]
Перечисления имеют пользовательский метакласс, который влияет на многие аспекты как производных классов Enum, так и их экземпляров (членов).
Классы Enum. Метакласс EnumType отвечает за предоставление __contains__(), __dir__(), __iter__() и других методов, которые позволяют делать такие вещи с классом Enum, которые терпят неудачу с типовым классом, таким как list(Color) или some_enum_var в Color. EnumType отвечает за то, чтобы различные другие методы в конечном классе Enum были корректными (такие как __new__(), __getnewargs__(), __str__() и __repr__()).
Классы Flag. Флаги имеют расширенный вид псевдонимизации: чтобы быть каноничным, значение флага должно быть степенью двойки, и не дублировать имя. Таким образом, в дополнение к определению псевдонима Enum, флаг без значения (также известный как 0) или с более чем одним значением степени двойки (например 3), считается псевдонимом.
Элементы Enum (также известные как экземпляры). Самое интересное в элементах enum: они являются синглтонами (singleton). EnumType создает их все, пока он создает сам класс enum, и затем помещает пользовательский __new__() по месту, чтобы гарантировать, что никакие новые экземпляры никогда не будут созданы, возвращая только существующие экземпляры-члены.
"В объектно-ориентированном программировании шаблон синглтона это шаблон разработки ПО, который ограничивает инстанциацию класса единственным экземпляром. Это один из известных шаблонов проектирования 'Банды четырех', который описывает, как решать повторяющиеся проблемы в объектно-ориентированном ПО. Подобный шаблон полезен, когда необходим ровно один объект для координации действий в системе.
Если более конкретно, то шаблон синглтона позволяет классам:
- гарантировать, что у них имеется только один экземпляр - предоставить простой доступ к экземпляру - управлять инстанциацией (например, скрывая конструктор класса).
Термин синглтона произошел из соответствующей математической концепции."
Элементы Flag. Элементы Flag могут быть возвращены итерацией, так же как класс Flag, и будут возвращены только канонические элементы. Например (обратите внимание, что не показаны BLACK, PURPLE и WHITE):
Инвертирование элемента флага возвратит соответствующее положительное значение вместо отрицательного значения, например:
>>> ~Color.RED
< Color.GREEN|BLUE: 6>
Элементы Flag имеют длину, соответствующую количеству значений степени двойки, которые они содержат. Например:
>>> len(Color.PURPLE) 2
[Enum Cookbook]
Хотя классы Enum, IntEnum, StrEnum, Flag и IntFlag покрывают большинство случаев использования, они не могут обслужить все возможные варианты применения. Ниже показаны рецепты для некоторых других типов перечислений, которые могут использоваться напрямую или в качестве примеров для создания ваших собственных классов.
Пропуск значений. Во многих случаях использования не имеет не важно, какое реальное значение у перечисления. Существует несколько способов определения такого простого типа перечисления:
• Использовать экземпляры auto для значения. • Использовать экземпляры object в качестве значения. • Использовать строку описания в качестве значения. • Использовать кортеж (tuple) в качестве значения, и пользовательский __new__() для замены кортежа на значение int.
Использование любого из этих методов означает для пользователя, что эти значения не важны, иа также позволяет добавлять, удалять или переупорядочивать элементы без необходимости перенумерации остальных элементов.
Использование auto. Для auto это может выглядеть так:
>>> classColor(Enum): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> Color.GREEN
< Color.GREEN: 3>
Использование object. Для object это может выглядеть так:
>>> classColor(Enum): ... RED = object() ... GREEN = object() ... BLUE = object() ... >>> Color.GREEN
< Color.GREEN: < objectobject at 0x...>>
Вот еще хороший пример, почему вы можете захотеть написать свой собственный __repr__():
>>> classColor(Enum): ... RED = object() ... GREEN = object() ... BLUE = object() ... def__repr__(self): ... return"< %s.%s>" % (self.__class__.__name__, self._name_) ... >>> Color.GREEN
< Color.GREEN>
Использование строки описания. Строка в качестве значения может выглядеть так:
>>> classColor(Enum): ... RED = 'stop' ... GREEN = 'go' ... BLUE = 'too fast!' ... >>> Color.GREEN
< Color.GREEN: 'go'>
Использование пользовательского __new__(). Автонумерация в __new__() может выглядеть так:
>>> classAutoNumber(Enum): ... def__new__(cls): ... value = len(cls.__members__) + 1 ... obj = object.__new__(cls) ... obj._value_ = value ... return obj ... >>> classColor(AutoNumber): ... RED = () ... GREEN = () ... BLUE = () ... >>> Color.GREEN
< Color.GREEN: 2>
Чтобы сделать AutoNumber более общим, добавьте в сигнатуру *args:
>>> classAutoNumber(Enum): ... def__new__(cls, *args): # единственное изменение предыдущего примера ... value = len(cls.__members__) + 1 ... obj = object.__new__(cls) ... obj._value_ = value ... return obj ...
Тогда если вы наследуете из AutoNumber, то можете написать свой собственный __init__ для обработки любых дополнительных аргументов:
Замечание: метод __new__(), если он определен, используется во время создания элементов Enum; он затем заменяется __new__() для Enum, который используется после создания класса для поиска существующих элементов.
Предупреждение: не вызывайте super().__new__(), поскольку будет найден только lookup-only __new__; вместо этого используйте тип данных напрямую, например:
obj = int.__new__(cls, value)
OrderedEnum. Упорядоченное перечисление, которое не основано на IntEnum, и поэтому поддерживает нормальные инварианты Enum (такие как не сравниваемые с другими перечислениями):
>>> classOrderedEnum(Enum): ... def__ge__(self, other): ... if self.__class__ is other.__class__: ... return self.value >= other.value ... returnNotImplemented ... def__gt__(self, other): ... if self.__class__ is other.__class__: ... return self.value > other.value ... returnNotImplemented ... def__le__(self, other): ... if self.__class__ is other.__class__: ... return self.value < = other.value ... returnNotImplemented ... def__lt__(self, other): ... if self.__class__ is other.__class__: ... return self.value < other.value ... returnNotImplemented ... >>> classGrade(OrderedEnum): ... A = 5 ... B = 4 ... C = 3 ... D = 2 ... F = 1 ... >>> Grade.C < Grade.A True
DuplicateFreeEnum. Вместо создания псевдонима выбросит ошибку, если было найдено дублированное значение элемента:
>>> classDuplicateFreeEnum(Enum): ... def__init__(self, *args): ... cls = self.__class__ ... ifany(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)) ... >>> classColor(DuplicateFreeEnum): ... RED = 1 ... GREEN = 2 ... BLUE = 3 ... GRENE = 2 ...
Traceback (most recent call last):
...
ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN'
Замечание: это полезный пример наследования Enum для добавления или изменения других поведений, а также для запрета псевдонимов. Если единственным желаемым изменением является запрет псеводонимов, то вместо этого можно просто использовать декоратор unique().
MultiValueEnum. Поддерживает возможность более одного значения на элементе:
TimePeriod. Пример отображения используемого атрибута _ignore_:
>>> from datetime import timedelta >>> classPeriod(timedelta, Enum): ... "different lengths of time" ... _ignore_ = 'Period i' ... Period = vars() ... for i inrange(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)>]
[Подклассы EnumType]
Хотя большинство потребностей в перечислении может быть удовлетворено путем настройки подклассов Enum, либо с помощью декораторов, либо с помощью пользовательских функций, класс EnumType может наследоваться для предоставления другого опыта использования Enum.