enum — Поддержка перечислений
Новое в версии 3.4.
Исходный код: Lib/enum.py
Перечисление — это набор символических имен (элементов), связанных с уникальными, постоянными значениями. Внутри перечисления элементы могут сравниваться по идентичности, а само перечисление может быть перебираемо.
8.13.1. Содержание модуля
Этот модуль определяет четыре класса перечислений, которые могут использоваться для определения уникальных наборов имен и значений: 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 -
Экземпляры заменяются соответствующим значением для элементов перечисления.
Новое в версии 3.6: Flag, IntFlag, auto
8.13.2. Создание перечисления
Перечисления создаются с использованием синтаксиса 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
8.13.3. Программный доступ к элементам перечисления и их атрибутам
Иногда полезно получать доступ к элементам перечислений программно (например, в ситуациях, где 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
8.13.4. Дублирование элементов и значений перечисления
Наличие двух элементов перечисления с одинаковым именем недопустимо:
>>> 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>
Примечание
Попытка создать элемент с тем же именем, что и уже определённый атрибут (другой элемент, метод и т.д.) или попытка создать атрибут с тем же именем, что и элемент, запрещена.
8.13.5. Обеспечение уникальности значений перечисления
По умолчанию перечисления разрешают множественные имена как псевдонимы для одного значения. Когда это поведение нежелательно, можно использовать следующий декоратор, чтобы гарантировать, что каждое значение используется только один раз в перечислении:
-
@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
8.13.6. Использование автоматических значений
Если точное значение не важно, можно использовать 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_() — предоставить следующее целое число int в последовательности с последним предоставленным значением int, но способ, которым это делается, является деталью реализации и может измениться.
8.13.7. Итерация
Итерация по элементам перечисления не предоставляет псевдонимы:
>>> 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']
8.13.8. Сравнения
Элементы перечисления сравниваются по идентичности:
>>> 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
8.13.9. Разрешенные элементы и атрибуты перечислений
В приведенных выше примерах для значений перечисления используются целые числа. Использование целых чисел кратко и удобно (и предоставляется по умолчанию функциональным 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__() и т. д.) и дескрипторов (методы также являются дескрипторами).
Примечание: если ваше перечисление определяет __new__() и/или __init__(), то любые значения, заданные члену перечисления, будут переданы в эти методы. См. Planet для примера.
8.13.10. Ограниченное наследование перечислений
Наследование перечислений разрешено только в том случае, если перечисление не определяет никаких членов. Поэтому это запрещено:
>>> 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 для примера.)
8.13.11. Сериализация
Перечисления можно сериализовать и десериализовать:
>>> from test.test_enum import Fruit >>> from pickle import dumps, loads >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO)) True
Применяются обычные ограничения для сериализации: сериализуемые перечисления должны быть определены на верхнем уровне модуля, так как для десериализации требуется, чтобы они были импортируемы из этого модуля.
Примечание
С протоколом сериализации 4 можно легко сериализовать перечисления, вложенные в другие классы.
Можно изменить способ сериализации/десериализации членов перечисления, определив __reduce_ex__() в классе перечисления.
8.13.12. Функциональный 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, заключается в том, что 1 — это логическое значение False, но все члены перечисления имеют значение True.
Сериализация перечислений, созданных с помощью функционального API, может быть сложной, так как используются детали реализации стека вызовов для попытки определения модуля, в котором создаётся перечисление (например, она потерпит неудачу, если вы используете вспомогательную функцию в отдельном модуле, а также может не работать в IronPython или Jython). Решением является явное указание имени модуля следующим образом:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
Предупреждение
Если module не указан, и перечисление не может определить его, новые члены перечисления не будут сериализуемы; для сохранения ошибок ближе к источнику, сериализация будет отключена.
Новый протокол сериализации 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)
- значение
-
То, что новый класс перечисления будет записывать как своё имя.
- имена
-
Члены перечисления. Это может быть строка, разделенная пробелами или запятыми (значения будут начинаться с 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} - модуль
-
Имя модуля, где можно найти новый класс перечисления.
- qualname
-
Местоположение нового класса перечисления в модуле.
- тип
-
Тип, который нужно добавить в новый класс перечисления.
- начало
-
Число, с которого нужно начать счёт, если переданы только имена.
Изменено в версии 3.5: Добавлен параметр start.
8.13.13. Производные перечисления
8.13.13.1. 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]
8.13.13.2. 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>
8.13.13.3. Флаг
Последний вариант — 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 не подойдут; например, при замене целочисленных констант перечислениями или для взаимодействия с другими системами.
8.13.13.4. Другие
Хотя 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.
8.13.14. Интересные примеры
Хотя Enum, IntEnum, IntFlag и Flag ожидаются для покрытия большинства случаев использования, они не могут охватить все. Вот рецепты для некоторых различных типов перечислений, которые можно использовать напрямую или как примеры для создания собственных.
8.13.14.1. Опускание значений
Во многих случаях использования значения перечисления не важны. Существует несколько способов определения такого типа простого перечисления:
- использование экземпляров
autoдля значения - использование экземпляров
objectв качестве значения - использование описательной строки в качестве значения
- использование кортежа в качестве значения и настраиваемого
__new__()для замены кортежа значениемint
Использование любого из этих методов сигнализирует пользователю, что эти значения не важны, а также позволяет добавлять, удалять или переупорядочивать члены без необходимости пересчитывать оставшиеся члены.
Какой бы метод вы ни выбрали, вы должны предоставить repr(), который также скрывает (неважное) значение:
>>> class NoValue(Enum): ... def __repr__(self): ... return '<%s.%s>' % (self.__class__.__name__, self.name) ...
8.13.14.1.1. Использование auto
Использование auto будет выглядеть так:
>>> class Color(NoValue): ... RED = auto() ... BLUE = auto() ... GREEN = auto() ... >>> Color.GREEN <Color.GREEN>
8.13.14.1.2. Использование object
Использование object будет выглядеть так:
>>> class Color(NoValue): ... RED = object() ... GREEN = object() ... BLUE = object() ... >>> Color.GREEN <Color.GREEN>
8.13.14.1.3. Использование описательной строки
Использование строки в качестве значения будет выглядеть так:
>>> class Color(NoValue): ... RED = 'stop' ... GREEN = 'go' ... BLUE = 'too fast!' ... >>> Color.GREEN <Color.GREEN> >>> Color.GREEN.value 'go'
8.13.14.1.4. Использование настраиваемого __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__(), если он определён, используется при создании членов Enum; затем он заменяется методом __new__() Enum, который используется после создания класса для поиска существующих членов.
8.13.14.2. 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
8.13.14.3. 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().
8.13.14.4. Планета
Если __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
8.13.15. В чём отличие перечислений?
Перечисления имеют пользовательский метакласс, который влияет на многие аспекты как производных классов Enum, так и их экземпляров (членов).
8.13.15.1. Классы перечислений
Метакласс EnumMeta отвечает за предоставление методов __contains__(), __dir__(), __iter__() и других, позволяющих работать с классом Enum так, как это не может быть сделано с обычным классом, например, с list(Color) или some_var in Color. Метакласс EnumMeta отвечает за корректную работу различных других методов конечного класса Enum (например, __new__(), __getnewargs__(), __str__() и __repr__()).
8.13.15.2. Элементы перечисления (или экземпляры)
Самое интересное в элементах перечисления заключается в том, что они являются синглтонами. EnumMeta создаёт их все во время создания класса Enum и затем устанавливает пользовательский метод __new__(), гарантируя, что новые экземпляры не будут созданы, а будут возвращаться только существующие элементы.
8.13.15.3. Подробные моменты
8.13.15.3.1. Поддерживаемые имена dunder
__members__ является OrderedDict из member_name:member элементов. Доступен только для класса.
__new__(), если указан, должен создавать и возвращать члены перечисления; также рекомендуется правильно установить атрибут _value_ элемента. После создания всех элементов он больше не используется.
8.13.15.3.2. Поддерживаемые имена sunder
-
_name_– имя элемента -
_value_– значение элемента; можно установить/изменить в__new__ -
_missing_– функция поиска, используемая при отсутствии значения; может быть переопределена -
_order_– используется в коде Python 2/3 для обеспечения согласованного порядка элементов (атрибут класса, удаляется во время создания класса) -
_generate_next_value_– используется функциональным API иautoдля получения соответствующего значения для элемента перечисления; может быть переопределён
Новое в версии 3.6: _missing_, _order_, _generate_next_value_
Для синхронизации кода 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_ необходим, так как порядок определения теряется до того, как он может быть записан.
8.13.15.3.3. Тип элемента перечисления
Элементы 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.
8.13.15.3.4. Булево значение классов и элементов Enum
Элементы Enum, смешанные с типами, отличными от Enum (такими как int, str и т.д.), оцениваются по правилам смешанного типа; в противном случае все элементы оцениваются как True. Чтобы сделать булеву оценку вашего собственного перечисления зависимой от значения элемента, добавьте следующее в ваш класс:
def __bool__(self):
return bool(self.value)
Классы Enum всегда оцениваются как True.
8.13.15.3.5. Классы Enum с методами
Если вы добавите дополнительные методы к классу 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']
8.13.15.3.6. Объединение элементов 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.6/library/enum.html