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() -
Декоратор класса Enum, который гарантирует, что только одно имя привязано к одному значению.
-
class enum.auto -
Экземпляры заменяются соответствующим значением для членов Enum. Начальное значение начинается с 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и т.д.)
Примечание
Несмотря на то, что для создания Enum мы используем синтаксис class, Enum не являются обычными классами Python. См. В чём отличие Enum? для получения более подробной информации.
Члены перечисления имеют удобочитаемые строковые представления:
>>> 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>
Если вы хотите получить доступ к членам enum по имени, используйте доступ по элементу:
>>> Color['RED'] <Color.RED: 1> >>> Color['GREEN'] <Color.GREEN: 2>
Если у вас есть член enum и вам нужно его 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 специально для перечислений. Он ищет __members__ перечисления, собирая обнаруженные псевдонимы; если таковые найдены, генерируется 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), но не строго обязательно. В подавляющем большинстве случаев значение перечисления не имеет значения. Но если значение важно, перечисления могут иметь произвольные значения.
Перечисления — это классы 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: Cannot extend enumerations
Но это разрешено:
>>> 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
Применяются обычные ограничения на сериализацию: сериализуемые перечисления должны быть определены на верхнем уровне модуля, так как для десериализации требуется, чтобы они были импортируемы из этого модуля.
Примечание
С протоколом сериализации pickle версии 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 в булевом смысле эквивалентно False, но все элементы перечисления оцениваются как True.
Сериализация перечислений, созданных с помощью функционального API, может быть сложной, так как для определения модуля, в котором создаётся перечисление, используются реализации стека фреймов (например, это не сработает, если вы используете вспомогательную функцию в отдельном модуле, а также может не сработать в IronPython или Jython). Решение заключается в явном указании имени модуля следующим образом:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
Предупреждение
Если module не указан, и Enum не может определить его, новые элементы перечисления не будут сериализуемы; для приближения ошибок к источнику сериализация будет отключена.
Новый протокол сериализации pickle 4 также в некоторых случаях полагается на __qualname__, который должен указывать местоположение, где pickle сможет найти класс. Например, если класс был доступен в классе SomeData в глобальной области видимости:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')
Полная сигнатура:
Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
- value
-
Имя, которое новый класс Enum будет регистрировать как своё имя.
- names
-
Элементы перечисления. Это может быть строка, разделённая пробелами или запятыми (значения начнутся с 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} - module
-
имя модуля, где можно найти новый класс Enum.
- qualname
-
местоположение в модуле, где можно найти новый класс Enum.
- type
-
тип, который нужно добавить в новый класс Enum.
- start
-
номер, с которого начинать счёт, если переданы только имена.
Изменено в версии 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
Последняя разновидность — 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выше. Это ограничение не распространяется на дополнения, которые добавляют только методы и не указывают другой тип данных, например,intилиstr. - Когда добавляется другой тип данных, атрибут
valueне является одинаковым с самим элементом перечисления, хотя он эквивалентен и сравнивается одинаково. - Форматирование с использованием %-символа:
%sи%rвызывают методыEnum__str__()и__repr__()соответственно; другие коды (например,%iили%hдля IntEnum) рассматривают элемент перечисления как тип, добавленный в него. - Форматируемые строковые литералы,
str.format()иformat()будут использовать метод__format__()добавленного типа. Если требуетсяEnumсо строковым представлениемstr()илиrepr(), используйте!sили!rкоды форматирования.
Интересные примеры
Хотя 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
Примечание
Метод __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'
Примечание
Это полезный пример для наследования от перечисления для добавления или изменения другого поведения, а также запрета псевдонимов. Если единственным желаемым изменением является запрет псевдонимов, можно использовать декоратор 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__ является OrderedDict member_name:member элементов. Он доступен только для класса.
__new__(), если указан, должен создавать и возвращать члены перечисления; также рекомендуется правильно установить _value_ члена. После создания всех членов он больше не используется.
Поддерживаемые имена _sunder_
-
_name_– имя члена -
_value_– значение члена; может быть установлено/изменено в__new__ -
_missing_– функция поиска, используемая при отсутствии значения; может быть переопределена -
_ignore_– список имён, либо какlist(), либо какstr(), которые не будут преобразованы в члены и будут удалены из итогового класса -
_order_– используется в коде Python 2/3 для обеспечения согласованности порядка членов (атрибут класса, удаляется во время создания класса) -
_generate_next_value_– используется функциональным API иautoдля получения соответствующего значения для члена перечисления; может быть переопределена
Введено в версии 3.6: _missing_, _order_, _generate_next_value_
Введено в версии 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_ необходим, так как порядок определения теряется перед его записью.
Тип члена перечисления
Члены класса 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.5.
Логическое значение классов и членов перечисления
Члены перечисления Enum, смешанные с типами, отличными от Enum (такими как int, str и т.д.), оцениваются по правилам смешанного типа; в противном случае все члены оцениваются как True. Чтобы сделать логическое значение вашего перечисления зависящим от значения члена, добавьте следующее в ваш класс:
def __bool__(self):
return bool(self.value)
Классы Enum всегда оцениваются как True.
Классы перечисления с методами
Если вы добавите к подклассу Enum дополнительные методы, как в классе Planet выше, эти методы будут отображаться в dir() члена, но не класса:
>>> dir(Planet) ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__'] >>> dir(Planet.EARTH) ['__class__', '__doc__', '__module__', 'name', '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>
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.7/library/enum.html