enum — Поддержка перечислений
В версии 3.4.
Исходный код: Lib/enum.py
Перечисление — это набор символических имен (членов), связанных с уникальными, постоянными значениями. В рамках перечисления члены можно сравнивать по идентичности, а само перечисление можно перебирать.
Примечание
Регистр имен членов перечислений
Поскольку перечисления используются для представления констант, рекомендуется использовать имена членов перечисления в формате ВЕРХНИЙ_РЕГИСТР, и мы будем использовать этот стиль в наших примерах.
Содержание модуля
Этот модуль определяет четыре класса перечислений, которые могут использоваться для определения уникальных наборов имен и значений: Enum, IntEnum, Flag и IntFlag. Он также определяет один декоратор, unique(), и один вспомогательный класс, auto.
-
class enum.Enum -
Базовый класс для создания перечисленных констант. Обратитесь к разделу Функциональный API для альтернативной синтаксической конструкции.
-
class enum.IntEnum -
Базовый класс для создания перечисленных констант, которые также являются подклассами
int.
-
class enum.IntFlag -
Базовый класс для создания перечисленных констант, которые могут объединяться с помощью побитовых операторов без потери членства в
IntFlag. ЧленыIntFlagтакже являются подклассамиint.
-
class enum.Flag -
Базовый класс для создания перечисленных констант, которые могут объединяться с помощью побитовых операций без потери членства в
Flag.
-
enum.unique() -
Декоратор класса перечисления, гарантирующий, что только одно имя привязано к одному значению.
-
class enum.auto -
Экземпляры заменяются соответствующим значением для членов перечисления. По умолчанию начальное значение начинается с 1.
В версии 3.6: Flag, IntFlag, auto
Создание перечисления
Перечисления создаются с помощью синтаксиса class, что делает их легкими для чтения и записи. Альтернативный метод создания описан в Функциональном API. Для определения перечисления необходимо унаследовать от Enum следующим образом:
>>> from enum import Enum >>> class Color(Enum): ... RED = 1 ... GREEN = 2 ... BLUE = 3 ...
Примечание
Значения членов перечисления
Значения членов могут быть любыми: int, str и т. д. Если точное значение не имеет значения, вы можете использовать экземпляры auto, и соответствующее значение будет выбрано за вас. Следует быть осторожным, если вы смешиваете auto с другими значениями.
Примечание
Номенклатура
- Класс
Colorявляется перечислением (или enum) - Атрибуты
Color.RED,Color.GREEN, и т. д. являются членами перечисления (или членами enum) и функционально являются константами. - Члены перечисления имеют имена и значения (имя
Color.RED—RED, значениеColor.BLUE—3и т. д.).
Примечание
Несмотря на то, что мы используем синтаксис class для создания перечислений, перечисления не являются обычными классами Python. Подробнее см. Чем перечисления отличаются?.
Члены перечисления имеют удобочитаемые строковые представления:
>>> print(Color.RED) Color.RED
…в то время как их repr содержит более подробную информацию:
>>> print(repr(Color.RED)) <Color.RED: 1>
Тип члена перечисления — это перечисление, к которому он принадлежит:
>>> type(Color.RED) <enum 'Color'> >>> isinstance(Color.GREEN, Color) True >>>
У членов перечисления также есть свойство, содержащее только имя элемента:
>>> print(Color.RED.name) RED
Перечисления поддерживают итерацию в порядке определения:
>>> class Shake(Enum): ... VANILLA = 7 ... CHOCOLATE = 4 ... COOKIES = 9 ... MINT = 3 ... >>> for shake in Shake: ... print(shake) ... Shake.VANILLA Shake.CHOCOLATE Shake.COOKIES Shake.MINT
Члены перечисления хешируемы, поэтому их можно использовать в словарях и множествах:
>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True
Программный доступ к членам перечисления и их атрибутам
Иногда полезно программно получать доступ к членам перечислений (например, в ситуациях, когда Color.RED не подойдет, так как точный цвет не известен на момент написания программы). Enum позволяет такой доступ:
>>> Color(1) <Color.RED: 1> >>> Color(3) <Color.BLUE: 3>
Если вы хотите получить доступ к членам перечисления по имени, используйте доступ по элементу:
>>> Color['RED'] <Color.RED: 1> >>> Color['GREEN'] <Color.GREEN: 2>
Если у вас есть член перечисления и вам нужно его name или value:
>>> member = Color.RED >>> member.name 'RED' >>> member.value 1
Дублирование членов и значений перечисления
Наличие двух членов перечисления с одинаковым именем недопустимо:
>>> class Shape(Enum): ... SQUARE = 2 ... SQUARE = 3 ... Traceback (most recent call last): ... TypeError: Attempted to reuse key: 'SQUARE'
Однако два члена перечисления могут иметь одинаковое значение. Учитывая два члена A и B с одинаковым значением (и A определен первым), B является псевдонимом A. Поиск значения A и B по значению вернет A. Поиск B по имени также вернет A:
>>> 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>
Примечание
Попытка создать член с таким же именем, как у уже определенного атрибута (другого члена, метода и т. д.), или попытка создать атрибут с таким же именем, как у члена, запрещена.
Обеспечение уникальных значений перечисления
По умолчанию перечисления позволяют использовать несколько имен в качестве псевдонимов для одного и того же значения. Когда такое поведение нежелательно, можно использовать следующий декоратор, чтобы убедиться, что каждое значение используется только один раз в перечислении:
-
@enum.unique
Декоратор class специально для перечислений. Он ищет псевдонимы в перечислении; если таковые обнаружены, генерируется ValueError с подробной информацией:
>>> 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:
>>> from enum import Enum, auto >>> class Color(Enum): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
Значения выбираются по _generate_next_value_(), который можно переопределить:
>>> class AutoName(Enum): ... def _generate_next_value_(name, start, count, last_values): ... return name ... >>> class Ordinal(AutoName): ... NORTH = auto() ... SOUTH = auto() ... EAST = auto() ... WEST = auto() ... >>> list(Ordinal) [<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]
Примечание
Цель метода по умолчанию _generate_next_value_() — предоставить следующее целое число в последовательности после последнего предоставленного, но способ его работы является деталью реализации и может измениться.
Примечание
Метод _generate_next_value_() должен быть определен до любых членов.
Итерация
Итерация по членам перечисления не предоставляет псевдонимы:
>>> list(Shape) [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
Специальный атрибут __members__ — это читаемое упорядоченное отображение имен к членам. Он включает все имена, определенные в перечислении, включая псевдонимы:
>>> 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']
Сравнения
Члены перечисления сравниваются по идентичности:
>>> Color.RED is Color.RED True >>> Color.RED is Color.BLUE False >>> Color.RED is not 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'
Однако сравнения на равенство определены:
>>> Color.BLUE == Color.RED False >>> Color.BLUE != Color.RED True >>> Color.BLUE == Color.BLUE True
Сравнение с неперечисляемыми значениями всегда вернет результат, что они не равны (снова, IntEnum был специально разработан для иного поведения, см. ниже):
>>> Color.BLUE == 2 False
Разрешенные члены и атрибуты перечислений
В приведенных выше примерах для значений перечисления используются целые числа. Использование целых чисел кратко и удобно (и предоставляется по умолчанию API «Функциональным API»), но не является строго обязательным. В подавляющем большинстве случаев значение перечисления не имеет особого значения. Но если значение важно, перечисления могут иметь произвольные значения.
Перечисления — это классы Python, и они могут иметь методы и специальные методы как обычно. Если у нас есть это перечисление:
>>> class Mood(Enum):
... FUNKY = 1
... HAPPY = 3
...
... def describe(self):
... # self is the member here
... return self.name, self.value
...
... def __str__(self):
... return 'my custom str! {0}'.format(self.value)
...
... @classmethod
... def favorite_mood(cls):
... # cls here is the enumeration
... return cls.HAPPY
...
Тогда:
>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'
Правила того, что разрешено, следующие: имена, начинающиеся и заканчивающиеся одним символом подчеркивания, зарезервированы перечислением и не могут быть использованы; все другие атрибуты, определенные в пределах перечисления, станут членами этого перечисления, за исключением специальных методов (__str__(), __add__(), и т.д.), дескрипторов (методы также являются дескрипторами) и имен переменных, перечисленных в _ignore_.
Примечание: если ваше перечисление определяет __new__() и/или __init__(), любые значения, заданные члену перечисления, будут переданы в эти методы. См. Planet для примера.
Ограниченное наследование перечислений
Новый класс Enum должен иметь один базовый класс Enum, не более одного конкретного типа данных и любое количество классов-миксов, основанных на object, по мере необходимости. Порядок этих базовых классов:
class EnumName([mix-in, ...,] [data-type,] base-enum):
pass
Кроме того, наследование перечисления разрешается только в том случае, если перечисление не определяет никаких членов. Поэтому это запрещено:
>>> class MoreColor(Color): ... PINK = 17 ... Traceback (most recent call last): ... TypeError: MoreColor: cannot extend enumeration 'Color'
Но это разрешено:
>>> class Foo(Enum): ... def some_behavior(self): ... pass ... >>> class Bar(Foo): ... HAPPY = 1 ... SAD = 2 ...
Разрешение наследования перечислений, которые определяют члены, приведет к нарушению некоторых важных инвариантов типов и экземпляров. С другой стороны, имеет смысл разрешить совместное использование некоторых общих свойств между группой перечислений. (См. OrderedEnum для примера.)
Сериализация
Перечисления можно сериализовать и десериализовать:
>>> from test.test_enum import Fruit >>> from pickle import dumps, loads >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO)) True
Применяются обычные ограничения для сериализации: сериализуемые перечисления должны быть определены в верхнем уровне модуля, поскольку десериализация требует их импортируемости из этого модуля.
Примечание
С протоколом сериализации 4 можно легко сериализовать перечисления, вложенные в другие классы.
Можно изменить способ сериализации/десериализации членов перечисления, определив __reduce_ex__() в классе перечисления.
Функциональный API
Класс Enum является вызываемым, предоставляя следующий функциональный API:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> Animal.ANT.value
1
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
Семантика этого API напоминает namedtuple. Первый аргумент вызова Enum — имя перечисления.
Второй аргумент — источник имен членов перечисления. Это может быть строка имен, разделенных пробелом, последовательность имен, последовательность пар имен/значений или отображение (например, словарь) имен на значения. Последние два варианта позволяют назначать произвольные значения перечислениям; другие автоматически назначают возрастающие целые числа, начиная с 1 (используйте параметр start для указания другого начального значения). Возвращается новый класс, производный от Enum. Другими словами, приведенное выше присвоение Animal эквивалентно:
>>> class Animal(Enum): ... ANT = 1 ... BEE = 2 ... CAT = 3 ... DOG = 4 ...
Причина, по которой по умолчанию используется 1 в качестве начального числа, а не 0, заключается в том, что 0 в булевом смысле, но все члены перечисления вычисляются как True.
Сериализация перечислений, созданных с помощью функционального API, может быть сложной, так как используются реализации стека вызовов для определения модуля, в котором создается перечисление (например, это не сработает, если вы используете служебную функцию в отдельном модуле, а также может не работать в IronPython или Jython). Решение заключается в явном указании имени модуля следующим образом:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
Предупреждение
Если module не указан, и Enum не может определить, что это такое, новые члены перечисления не будут сериализуемы; чтобы сохранить ошибки ближе к источнику, сериализация будет отключена.
Новый протокол сериализации 4 также в некоторых случаях полагается на __qualname__, чтобы быть установленным в местоположение, где сериализация сможет найти класс. Например, если класс был доступен в классе SomeData в глобальной области:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')
Полная сигнатура:
Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
- значение
-
То, что новый класс Enum будет записывать как свое имя.
- имена
-
Члены Enum. Это может быть строка имен, разделенных пробелом или запятой (значения будут начинаться с 1, если не указано иное):
'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
или итератор имен:
['RED', 'GREEN', 'BLUE']
или итератор пар (имя, значение):
[('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]или отображение:
{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42} - модуль
-
Имя модуля, где можно найти новый класс Enum.
- qualname
-
Где в модуле можно найти новый класс Enum.
- тип
-
Тип, который необходимо добавить в новый класс Enum.
- начало
-
Число, с которого следует начать подсчет, если переданы только имена.
Изменено в версии 3.5: Добавлен параметр start.
Производные перечисления
IntEnum
Первая вариация Enum, предоставляемая, также является подклассом int. Члены IntEnum могут сравниваться с целыми числами; в результате перечисления целых чисел разных типов также могут сравниваться друг с другом:
>>> from enum import IntEnum >>> class Shape(IntEnum): ... CIRCLE = 1 ... SQUARE = 2 ... >>> class Request(IntEnum): ... POST = 1 ... GET = 2 ... >>> Shape == 1 False >>> Shape.CIRCLE == 1 True >>> Shape.CIRCLE == Request.POST True
Однако, они по-прежнему не могут сравниваться со стандартными перечислениями Enum:
>>> class Shape(IntEnum): ... CIRCLE = 1 ... SQUARE = 2 ... >>> class Color(Enum): ... RED = 1 ... GREEN = 2 ... >>> Shape.CIRCLE == Color.RED False
Значения IntEnum ведут себя как целые числа иными ожидаемыми способами:
>>> int(Shape.CIRCLE) 1 >>> ['a', 'b', 'c'][Shape.CIRCLE] 'b' >>> [i for i in range(Shape.SQUARE)] [0, 1]
IntFlag
Следующая вариация Enum, предоставляемая, IntFlag, также основана на int. Различие заключается в том, что члены IntFlag могут комбинироваться с использованием побитовых операторов (&, |, ^, ~), и результатом по-прежнему является член IntFlag. Однако, как следует из названия, члены IntFlag также являются подклассами int и могут использоваться там, где используется int. Любая операция над членом IntFlag, кроме побитовых операций, потеряет членство в IntFlag.
Новое в версии 3.6.
Пример класса IntFlag:
>>> from enum import IntFlag >>> class Perm(IntFlag): ... R = 4 ... W = 2 ... X = 1 ... >>> Perm.R | Perm.W <Perm.R|W: 6> >>> Perm.R + Perm.W 6 >>> RW = Perm.R | Perm.W >>> Perm.R in RW True
Также возможно присваивать имена комбинациям:
>>> class Perm(IntFlag): ... R = 4 ... W = 2 ... X = 1 ... RWX = 7 >>> Perm.RWX <Perm.RWX: 7> >>> ~Perm.RWX <Perm.-8: -8>
Еще одно важное различие между IntFlag и Enum заключается в том, что если флаги не установлены (значение равно 0), ее логическое значение равно False:
>>> Perm.R & Perm.X <Perm.0: 0> >>> bool(Perm.R & Perm.X) False
Поскольку члены IntFlag также являются подклассами int, они могут комбинироваться с ними:
>>> Perm.X | 8 <Perm.8|X: 9>
Флаг
Последняя вариация — Flag. Как и IntFlag, члены Flag могут комбинироваться с использованием побитовых операторов (&, |, ^, ~). В отличие от IntFlag, их нельзя комбинировать ни с каким другим перечислением Flag, ни с int. Хотя можно указать значения напрямую, рекомендуется использовать auto в качестве значения и позволить Flag выбрать подходящее значение.
Новое в версии 3.6.
Как и IntFlag, если комбинация членов Flag не устанавливает никаких флагов, ее логическое значение равно False:
>>> from enum import Flag, auto >>> class Color(Flag): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> Color.RED & Color.GREEN <Color.0: 0> >>> bool(Color.RED & Color.GREEN) False
Индивидуальные флаги должны иметь значения, являющиеся степенями двойки (1, 2, 4, 8, …), в то время как комбинации флагов — нет:
>>> class Color(Flag): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... WHITE = RED | BLUE | GREEN ... >>> Color.WHITE <Color.WHITE: 7>
Давая имя условию «флаги не установлены», его логическое значение не меняется:
>>> class Color(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, ее реализацию можно было бы легко реализовать независимо:
class IntEnum(int, Enum):
pass
Это демонстрирует, как можно определять подобные производные перечисления; например, StrEnum, который включает в себя str вместо int.
Некоторые правила:
- При наследовании от
Enum, типы миксинов должны располагаться перед самимEnumв последовательности базовых классов, как в примереIntEnumвыше. - Хотя у
Enumмогут быть члены любого типа, после добавления дополнительного типа все члены должны иметь значения этого типа, например,intвыше. Это ограничение не относится к миксинам, которые добавляют только методы и не указывают другой тип. - Когда добавляется другой тип данных, атрибут
valueне такой же, как сам член перечисления, хотя он эквивалентен и будет сравниваться как равный. - Форматирование по шаблону %:
%sи%rвызывают методы__str__()и__repr__()классаEnumсоответственно; другие кодовые фрагменты (например,%iили%hдля IntEnum) обрабатывают член перечисления как тип его миксина. -
Строки форматирования,
str.format()иformat()будут использовать тип миксина__format__(), если не переопределены__str__()или__format__()в подклассе, в этом случае будут использоваться переопределенные методы или методы классаEnum. Используйте коды форматирования !s и !r, чтобы принудительно использовать методы__str__()и__repr__()классаEnum.
Когда использовать __new__() vs. __init__()
__new__() необходимо использовать всякий раз, когда требуется настроить фактическое значение члена Enum. Любые другие изменения могут быть внесены либо в __new__() или __init__(), при этом __init__() предпочтительнее.
Например, если вы хотите передать несколько элементов в конструктор, но только один из них должен быть значением:
>>> class Coordinate(bytes, Enum): ... """ ... Coordinate with binary codes that can be indexed by the int code. ... """ ... 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
Интересные примеры
Хотя Enum, IntEnum, IntFlag и Flag должны охватывать большинство случаев использования, они не могут охватить все. Вот рецепты для некоторых различных типов перечислений, которые можно использовать непосредственно или в качестве примеров для создания собственных.
Пропуск значений
Во многих случаях значения перечисления не важны. Существует несколько способов определения такого типа простого перечисления:
- использовать экземпляры
autoдля значения - использовать экземпляры
objectв качестве значения - использовать описательный строковый литерал в качестве значения
- использовать кортеж в качестве значения и пользовательский
__new__()для замены кортежа значением типаint
Использование любого из этих методов указывает пользователю, что эти значения не важны, и также позволяет добавлять, удалять или менять порядок элементов без необходимости перенумерации оставшихся элементов.
Какой бы метод вы ни выбрали, вам следует предоставить repr(), который также скрывает (неважное) значение:
>>> class NoValue(Enum): ... def __repr__(self): ... return '<%s.%s>' % (self.__class__.__name__, self.name) ...
Использование auto
Использование auto будет выглядеть так:
>>> class Color(NoValue): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> Color.GREEN <Color.GREEN>
Использование object
Использование object будет выглядеть так:
>>> class Color(NoValue): ... RED = object() ... GREEN = object() ... BLUE = object() ... >>> Color.GREEN <Color.GREEN>
Использование описательной строки
Использование строки в качестве значения будет выглядеть так:
>>> class Color(NoValue): ... RED = 'stop' ... GREEN = 'go' ... BLUE = 'too fast!' ... >>> Color.GREEN <Color.GREEN> >>> Color.GREEN.value 'go'
Использование пользовательского __new__()
Использование автонумерации __new__() будет выглядеть так:
>>> class AutoNumber(NoValue): ... def __new__(cls): ... value = len(cls.__members__) + 1 ... obj = object.__new__(cls) ... obj._value_ = value ... return obj ... >>> class Color(AutoNumber): ... RED = () ... GREEN = () ... BLUE = () ... >>> Color.GREEN <Color.GREEN> >>> Color.GREEN.value 2
Чтобы создать более универсальный AutoNumber, добавьте *args в сигнатуру:
>>> class AutoNumber(NoValue): ... def __new__(cls, *args): # this is the only change from above ... value = len(cls.__members__) + 1 ... obj = object.__new__(cls) ... obj._value_ = value ... return obj ...
Затем, при наследовании от AutoNumber вы можете написать свой собственный __init__ для обработки дополнительных аргументов:
>>> class Swatch(AutoNumber): ... def __init__(self, pantone='unknown'): ... self.pantone = pantone ... AUBURN = '3497' ... SEA_GREEN = '1246' ... BLEACHED_CORAL = () # New color, no Pantone code yet! ... >>> Swatch.SEA_GREEN <Swatch.SEA_GREEN> >>> Swatch.SEA_GREEN.pantone '1246' >>> Swatch.BLEACHED_CORAL.pantone 'unknown'
Примечание
Метод __new__(), если определён, используется при создании членов перечисления; затем он заменяется методом __new__() перечисления, который используется после создания класса для поиска существующих членов.
OrderedEnum
Упорядоченное перечисление, которое не основано на IntEnum и, следовательно, сохраняет обычные инварианты Enum (например, не сравнимо с другими перечислениями):
>>> 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
Вызывает ошибку, если найдено дублирующееся имя члена, вместо создания псевдонима:
>>> 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().
Планета
Если __new__() или __init__() определены, значение члена перечисления будет передано в эти методы:
>>> 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 # in kilograms ... self.radius = radius # in meters ... @property ... def surface_gravity(self): ... # universal gravitational constant (m3 kg-1 s-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
Период времени
Пример использования атрибута _ignore_:
>>> from datetime import timedelta >>> class Period(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)>]
Как перечисления отличаются?
Перечисления имеют пользовательский метакласс, который влияет на многие аспекты как производных классов перечислений, так и их экземпляров (членов).
Классы перечислений
Метакласс EnumMeta отвечает за предоставление __contains__(), __dir__(), __iter__() и других методов, которые позволяют делать с классом Enum вещи, которые не работают с обычным классом, например, list(Color) или some_enum_var in Color. EnumMeta отвечает за обеспечение правильности различных других методов в окончательном классе Enum (таких как __new__(), __getnewargs__(), __str__() и __repr__()).
Члены перечисления (также экземпляры)
Самое интересное в членах перечисления заключается в том, что они являются синглтонами. EnumMeta создает их все, когда создается класс Enum, и затем устанавливает пользовательский __new__() для обеспечения того, что новые экземпляры никогда не создаются, возвращая только существующие члены.
Дополнительные замечания
Поддерживаемые __dunder__ имена
__members__ — это только для чтения упорядоченное отображение member_name:member элементов. Оно доступно только для класса.
__new__(), если указано, должно создавать и возвращать члены перечисления; также рекомендуется установить значение _value_ члена соответствующим образом. После создания всех членов оно больше не используется.
Поддерживаемые _sunder_ имена
_name_— имя члена_value_— значение члена; может быть установлено/изменено в__new___missing_— функция поиска, используемая, когда значение не найдено; может быть переопределена_ignore_— список имён, либо какlist, либо какstr, которые не будут преобразованы в члены и будут удалены из конечного класса_order_— используется в коде Python 2/3 для обеспечения согласованного порядка членов (атрибут класса, удаляется во время создания класса)_generate_next_value_— используется функциональным API иautoдля получения соответствующего значения для члена перечисления; может быть переопределена
New in version 3.6: _missing_, _order_, _generate_next_value_
New in version 3.7: _ignore_
Для синхронизации кода Python 2/Python 3 можно предоставить атрибут _order_. Он будет проверен по отношению к фактическому порядку перечисления, и если они не совпадают, будет выдано сообщение об ошибке:
>>> class Color(Enum): ... _order_ = 'RED GREEN BLUE' ... RED = 1 ... BLUE = 3 ... GREEN = 2 ... Traceback (most recent call last): ... TypeError: member order does not match _order_
Примечание
В коде Python 2 атрибут _order_ необходим, так как порядок определения теряется до того, как он может быть записан.
_Приватные__имена
Приватные имена будут обычными атрибутами в Python 3.11 вместо ошибки или члена (в зависимости от того, заканчивается ли имя символом подчёркивания). Использование таких имён в 3.10 вызовет DeprecationWarning.
Тип члена Enum
Enum члены являются экземплярами их класса Enum и обычно обращаются к ним как EnumClass.member. В некоторых случаях к ним также можно обратиться как EnumClass.member.member, но делать этого не следует, так как такой поиск может завершиться неудачей или, что ещё хуже, вернуть что-то помимо члена Enum, который вы ищете (ещё одна причина использовать все заглавные буквы для имён членов):
>>> class FieldTypes(Enum): ... name = 0 ... value = 1 ... size = 2 ... >>> FieldTypes.value.size <FieldTypes.size: 2> >>> FieldTypes.size.value 2
Примечание
Это поведение устарело и будет удалено в 3.11.
Изменено в версии 3.5.
Логическое значение классов и членов Enum
Enum члены, которые смешаны с типами, отличными от Enum (например, int, str и т.д.), оцениваются по правилам смешанного типа; в противном случае все члены оцениваются как True. Чтобы сделать логическое значение вашего собственного перечисления зависящим от значения члена, добавьте следующее в свой класс:
def __bool__(self):
return bool(self.value)
Классы Enum с методами
Если вы добавите классу Enum дополнительные методы, например, класс Planet выше, эти методы будут отображаться в dir() члена, но не класса:
>>> 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
Если комбинация флаговых членов не именована, в repr() будут включены все именованные флаги и все именованные комбинации флагов, которые находятся в значении:
>>> class Color(Flag): ... RED = auto() ... GREEN = auto() ... BLUE = auto() ... MAGENTA = RED | BLUE ... YELLOW = RED | GREEN ... CYAN = GREEN | BLUE ... >>> Color(3) # named combination <Color.YELLOW: 3> >>> Color(7) # not named combination <Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>
Примечание
В 3.11 безымянные комбинации флагов будут генерировать только канонические флаги-члены (то есть флаги с единственным значением). Таким образом, Color(7) будет генерировать что-то вроде <Color.BLUE|GREEN|RED: 7>.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/enum.html