Документация Python неофициальный перевод
Содержание страницы

8.13. enum – Поддержка перечисленийenum – Support for enumerations

Новое в версии 3.4.

Исходный код: Lib/enum.py


Перечисление – это набор символических имён (членов), привязанных к уникальным постоянным значениям. Внутри перечисления члены можно сравнивать по идентичности, а само перечисление можно перебирать.

8.13.1. Содержание модуляModule Contents

Этот модуль определяет четыре класса перечислений, которые можно использовать для определения уникальных наборов имён и значений: 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. Создание перечисленияCreating an Enum

Перечисления создаются с использованием синтаксиса class, что делает их легко читаемыми и удобными для записи. Альтернативный метод создания описан в Функциональный API. Чтобы определить перечисление, создайте подкласс Enum следующим образом:

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...

Примечание

Значения членов Enum

Значения членов могут быть любыми: int, str и т.д. Если точное значение неважно, можно использовать экземпляры auto, и подходящее значение будет выбрано автоматически. Следует соблюдать осторожность при смешивании auto с другими значениями.

Примечание

Терминология

  • Класс Color является перечислением (или enum)

  • Атрибуты Color.RED, Color.GREEN и т.д. являются членами перечисления (или членами перечисления) и по сути являются константами.

  • Члены перечисления имеют имена и значения (имя Color.REDRED, значение Color.BLUE3 и т.д.)

Примечание

Хотя для создания перечислений мы используем синтаксис 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. Программный доступ к членам перечисления и их атрибутамProgrammatic access to enumeration members and their attributes

Иногда бывает полезно обращаться к членам перечисления программно (например, в ситуациях, когда 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. Дублирование членов и значений перечисленияDuplicating enum members and values

Наличие двух членов перечисления с одинаковым именем недопустимо:

>>> 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. Обеспечение уникальности значений перечисленияEnsuring unique enumeration values

По умолчанию перечисления допускают несколько имён в качестве псевдонимов для одного и того же значения. Если такое поведение нежелательно, можно использовать следующий декоратор, чтобы гарантировать, что каждое значение используется в перечислении только один раз:

@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. Использование автоматических значенийUsing automatic values

Если точное значение неважно, можно использовать 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. ИтерацияIteration

Перебор членов перечисления не включает псевдонимы:

>>> 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. СравненияComparisons

Члены перечисления сравниваются по идентичности:

>>> 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. Допустимые члены и атрибуты перечисленийAllowed members and attributes of enumerations

В примерах выше для значений перечислений используются целые числа. Использование целых чисел – это кратко и удобно (и предусмотрено по умолчанию функциональным API), но не является строгим требованием. В подавляющем большинстве случаев не важно, каково фактическое значение элемента перечисления. Но если значение является важным, перечисления могут иметь произвольные значения.

Перечисления – это классы Python и могут иметь обычные методы и специальные методы. Если есть такое перечисление:

>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self – это член перечисления
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls здесь – это перечисление
...         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. Ограниченное создание подклассов перечисленийRestricted subclassing of enumerations

Создание подкласса перечисления разрешено только в том случае, если перечисление не определяет никаких членов. Так делать запрещено:

>>> 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. Сериализация (pickling)Pickling

Перечисления могут быть упакованы и распакованы с помощью модуля pickle.

>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True

Обычные ограничения для упаковки с помощью модуля pickle применяются: перечисления, которые можно упаковать, должны быть определены на верхнем уровне модуля, так как для распаковки их нужно импортировать из этого модуля.

Примечание

С протоколом pickle версии 4 можно легко упаковывать перечисления, вложенные в другие классы.

Можно изменить способ сериализации/десериализации членов Enum, определив __reduce_ex__() в классе перечисления.

8.13.12. Функциональный APIFunctional 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 не может его определить, новые члены Enum не будут поддерживать распаковку; чтобы ошибки были ближе к источнику, pickling будет отключён.

Новый протокол 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)
значение

Что новый класс 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.

start

число, с которого начинать отсчёт, если переданы только имена.

Изменено в версии 3.5: Добавлен параметр start.

8.13.13. Производные перечисленияDerived Enumerations

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

Последняя разновидность – 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. ПрочиеOthers

Хотя IntEnum является частью модуля enum, его очень просто реализовать самостоятельно:

class IntEnum(int, Enum):
    pass

Это показывает, как можно определить подобные производные перечисления; например, StrEnum, в который примешивается str вместо int.

Некоторые правила:

  1. При создании подкласса Enum типы-примеси должны располагаться перед самим Enum в последовательности базовых классов, как в IntEnum примере выше.

  2. Хотя Enum может содержать элементы любого типа, как только к нему примешивается дополнительный тип, все элементы должны иметь значения этого типа, например int выше. Это ограничение не распространяется на примеси, которые только добавляют методы и не указывают другой тип данных, такие как int или str.

  3. При примешивании другого типа данных атрибут value отличается от самого члена перечисления, хотя он эквивалентен и будет равным при сравнении.

  4. Форматирование в стиле %: %s и %r вызывают методы __str__() и __repr__() класса Enum соответственно; другие коды (например, %i или %h для IntEnum) обрабатывают член перечисления как его примешанный тип.

  5. Форматированные строковые литералы, str.format(), и format() будут использовать __format__() примешанного типа. Если требуется str() или repr() класса Enum, используйте коды форматирования !s или !r.

8.13.14. Интересные примерыInteresting examples

Хотя ожидается, что Enum, IntEnum, IntFlag и Flag покрывают большинство случаев использования, они не могут охватить их все. Ниже приведены рецепты для некоторых других типов перечислений, которые можно использовать напрямую или как примеры для создания собственных.

8.13.14.1. Пропуск значенийOmitting values

Во многих случаях использования неважно, каково фактическое значение элемента перечисления. Есть несколько способов определить такой простой тип перечисления:

  • использовать экземпляры auto в качестве значения

  • использовать экземпляры object в качестве значения

  • использовать описательную строку в качестве значения

  • использовать кортеж в качестве значения и собственный __new__() для замены кортежа на значение int

Использование любого из этих методов показывает пользователю, что эти значения не важны, а также позволяет добавлять, удалять или переупорядочивать члены без необходимости перенумерации остальных членов.

Какой бы метод ни был выбран, следует предоставить repr(), который также скрывает (неважное) значение:

>>> class NoValue(Enum):
...     def __repr__(self):
...         return '<%s.%s>' % (self.__class__.__name__, self.name)
...

8.13.14.1.1. Использование autoUsing auto

Использование auto будет выглядеть так:

>>> class Color(NoValue):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN>

8.13.14.1.2. Использование objectUsing object

Использование object будет выглядеть так:

>>> class Color(NoValue):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN>

8.13.14.1.3. Использование описательной строкиUsing a descriptive string

Использование строки в качестве значения будет выглядеть так:

>>> class Color(NoValue):
...     RED = 'stop'
...     GREEN = 'go'
...     BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
'go'

8.13.14.1.4. Использование настраиваемого __new__()Using a custom __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. Planet

Если определены __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       # в килограммах
...         self.radius = radius   # в метрах
...     @property
...     def surface_gravity(self):
...         # гравитационная постоянная (м3 кг-1 с-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. Чем отличаются перечисления?How are Enums different?

Перечисления имеют собственный метакласс, который влияет на многие аспекты как производных классов Enum, так и их экземпляров (членов).

8.13.15.1. Классы перечисленийEnum Classes

Мета-класс EnumMeta отвечает за предоставление __contains__(), __dir__(), __iter__() и других методов, которые позволяют выполнять с классом Enum действия, которые на обычном классе не сработают, например list(Color) или some_var in Color. EnumMeta отвечает за обеспечение того, чтобы различные другие методы итогового класса Enum были корректны (таких как __new__(), __getnewargs__(), __str__() и __repr__()).

8.13.15.2. Элементы перечисления (также экземпляры)Enum Members (aka instances)

Самое интересное в членах Enum – это то, что они являются синглтонами. EnumMeta создаёт их все при создании самого класса Enum, а затем помещает на место настраиваемый __new__(), чтобы гарантировать, что новые экземпляры никогда не будут созданы, возвращая только существующие экземпляры членов.

8.13.15.3. ТонкостиFiner Points

8.13.15.3.1. Поддерживаемые __dunder__ именаSupported __dunder__ names

__members__ – это OrderedDict из member_name:member элементов. Он доступен только у класса.

__new__(), если указан, должен создавать и возвращать элементы перечисления; также рекомендуется соответствующим образом установить _value_ элемента. После создания всех элементов он больше не используется.

8.13.15.3.2. Поддерживаемые _sunder_ именаSupported _sunder_ names

  • _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 member type

Члены 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 классов и элементовBoolean value of Enum classes and members

Члены Enum, смешанные с не-Enum типами (такими как int, str и т.п.), вычисляются по правилам примешанного типа; в противном случае все члены вычисляются как True. Чтобы логическое вычисление вашего собственного Enum зависело от значения члена, добавьте в свой класс следующее:

def __bool__(self):
    return bool(self.value)

Классы Enum всегда вычисляются как True.

8.13.15.3.5. Enum классы с методамиEnum classes with methods

Если добавить подклассу 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. Объединение элементов FlagCombining members of Flag

Если комбинация членов Flag не имеет имени, repr() будет включать все именованные флаги и все именованные комбинации флагов, присутствующие в значении:

>>> class Color(Flag):
...     RED = auto()
...     GREEN = auto()
...     BLUE = auto()
...     MAGENTA = RED | BLUE
...     YELLOW = RED | GREEN
...     CYAN = GREEN | BLUE
...
>>> Color(3)  # именованная комбинация
<Color.YELLOW: 3>
>>> Color(7)      # не именованная комбинация
<Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>