enum
— Поддержка перечислений¶
Добавлено в версии 3.4.
Исходный код: Lib/enum.py
Перечисление - это набор символьных имен (полей), привязанных к уникальным постоянным значением. В перечислении поля можно сравнивать по идентификатору, а само перечисление можно итерировать.
Примечание
Регистр членов Enum
Поскольку перечисления используются для представления констант, мы рекомендуем использовать имена UPPER_CASE для членов перечисления и будем использовать этот стиль в наших примерах.
Содержание модуля¶
Модуль определяет четыре класса перечисления, которые могут
использоваться для определения уникальных наборов имен и значений: 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
Создание Enum¶
Перечисления создаются с помощью синтаксиса class
, который упрощает их
чтение и запись. Альтернативный способ создания описан в разделе Функциональный API.
Определить перечисление, подкласс Enum
следующим образом:
>>> from enum import Enum
>>> class Color(Enum):
... RED = 1
... GREEN = 2
... BLUE = 3
...
Примечание
Перечислите значений поля
Членом значения может быть что угодно: int
, str
и т.д.. Если
точное значение неважно, вы можете использовать auto
сущности и для
вас будет выбран подходящий значение. Если вы смешиваете auto
с
другими значения, необходимо соблюдать осторожность.
Примечание
Номенклатура
- Класс
Color
является enumeration (или enum) - Атрибуты
Color.RED
,Color.GREEN
и т.д. являются enumeration members (или enum members) и функционально постоянными. - поля перечисления имеют names и values (название
Color.RED
-RED
, значениеColor.BLUE
-3
и т. д.)
Примечание
Даже при том, что мы используем синтаксис class
, чтобы создать Enums, Enums
не нормальные классы Python. Дополнительные сведения см. в разделе
Чем Enums отличаются?.
Члены перечисления имеют читаемые человеком представления строка:
>>> print(Color.RED)
Color.RED
… в то время как их repr
имеет больше информации:
>>> print(repr(Color.RED))
<Color.RED: 1>
type поля перечисления является перечислением, которому он принадлежит:
>>> 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>
Если вы хотите получить доступ к полям перечисления по name, используйте доступ к элементам:
>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>
Если у вас есть поле перечисления и вам нужен его name
или value
:
>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1
Дублирование элементов enum и значения¶
Наличие двух полей перечисления с одинаковым именем недопустимо:
>>> class Shape(Enum):
... SQUARE = 2
... SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'SQUARE'
Однако два элемента перечисления имеют одинаковую значение. Учитывая два поля A и B с одним и тем же значение (и A определено первым), B является алиас к A. By-значение поиск значение 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>
Примечание
Пытаясь создать поля с тем же именем как уже определенный атрибут (другой поле, метод, и т.д.) или пытаясь создать атрибут с тем же именем, поскольку полю не разрешают.
Обеспечение уникального перечисления значения¶
По умолчанию перечисления позволяют несколько имен как псевдонимы для того же значение. Когда это поведение не желаемо, следующий декоратор может быть используемый, чтобы гарантировать, что каждый значение - используемый только однажды в enumeration:
-
@
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_()
по умолчанию является предоставление следующего
int
в последовательности с последним предоставленным int
, но
способ его выполнения является детализацией реализации и может измениться.
Примечание
Метод _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
Упорядоченные сравнения значения перечисления не поддерживаются. Члены Enum не целые числа (но см. 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), но не строго соблюдается. В подавляющем большинстве случаев использования не важно, какова фактическая значение перечисления. Но если важный значение is, у перечислений может быть произвольный значения.
Перечисления являются классами 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__()
тогда
независимо от того, что значение(я) были даны enum полю, будет передан в те
методы. Пример см. в разделе Планета.
Ограниченная подклассификация Enum¶
Новый класс Enum
должен иметь один базовый класс Enum, до одного
конкретного типа данных и столько классов object
-based mixin, сколько
необходимо. Порядок этих базовых классов равен:
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
Обычные ограничения для пиклинг применяются: picklable enums должен быть определен в высшем уровне модуля, так как несоление требует, чтобы они были разрешены к ввозу от того модуля.
Примечание
С помощью протокола pickle версии 4 можно легко pickle элементы, вложенные в другие классы.
Можно изменить способ подбора/отмены выбора элементов Enum путем определения
__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
является имя перечисления.
Второй аргумент - это source имен полей перечисления. Это может быть
отделенный от пробела строка имен, последовательность имен,
последовательность 2 кортежей с key/значение парами или отображение (например,
словарь) имен к значения. Последние два варианта позволяют назначать
произвольные значения перечислениям; другие автоматически назначают
увеличивающиеся целые числа, начиная с 1 (используйте параметр start
,
чтобы указать другой начальный значение). Возвращается новый класс,
производный от Enum
. Другими словами, вышеуказанное присвоение
Animal
эквивалентно:
>>> class Animal(Enum):
... ANT = 1
... BEE = 2
... CAT = 3
... DOG = 4
...
Причина невыполнения обязательств к 1
как стартовое число и не
0
состоит в том, что 0
- False
в булевом смысле, но
enum поля, которых все оценивают к True
.
Черпающие перечисления, созданные с помощью функционального API, могут быть сложными, так как детали реализации стека кадров используемый пытаться выяснить, в каком модуле создается перечисление (например, если вы используете служебную функцию в отдельном модуле, а также можете не работать с IronPython или Jython). Решение заключается в явном указании имени модуля следующим образом:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
Предупреждение
Если module
не предоставляется и Enum не может определить, что это такое,
новые поля Enum не могут быть отменены; чтобы сохранить ошибки ближе к
источнику, пиклинг будет отключен.
Новый протокол 4 pickle также, при некоторых обстоятельствах, полагается на
__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
. Как и 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
атрибут - не то же самое как enum поле сам, хотя это эквивалентно и выдержит сравнение равный. - % - форматирование стиля: %s и %r называют класс
__str__()
и__repr__()
Enum
соответственно; другие коды (например, %i или %h для IntEnum) рассматривают поле перечисления как его смешанный тип. - Отформатированные строковые литералы,
str.format()
иformat()
будут использовать__format__()
смешанного типа, если__str__()
или__format__()
не переопределены в подклассе, и в этом случае будут использоваться переопределенные методы или методыEnum
. Используйте коды формата !s и !r, чтобы принудительно использовать методы__str__()
и__repr__()
классаEnum
.
Когда использовать __new__()
против __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): # это единственное изменение сверху
... 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 = () # Новый цвет, кода Pantone пока нет!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'
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
Реплицируемый Enum¶
Поднимает ошибку, если двойное имя поля найдено вместо того, чтобы создать алиас:
>>> 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
TimePeriod¶
Пример использования _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)>]
Чем Enums отличаются?¶
Перечисления имеют пользовательский метакласс, который затрагивает многие аспекты как производных классов Enum, так и их сущности (полей).
Классы Enum¶
EnumMeta
метакласс отвечает за предоставление методов __contains__()
,
__dir__()
, __iter__()
и других методов, которые позволяют выполнять действия с
классом Enum
, отказавшим в обычном классе, например list(Color)
или some_enum_var in Color.
EnumMeta
отвечает за обеспечение правильности
различных других методов для конечного класса Enum
(таких как __new__()
,
__getnewargs__()
, __str__()
и __repr__()
).
Члены перечисления (иначе сущности)¶
Самое интересное в полях Enum то, что они одиночки. 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
для получения соответствующего значение для элемента перечисления; может быть переопределен
Добавлено в версии 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
поля являются сущности их класса Enum
и обычно доступны
как EnumClass.member
. При определенных обстоятельствах к ним можно также получить
доступ как EnumClass.member.member
, но вы никогда не должны делать этого, поскольку тот
поиск может потерпеть неудачу или, хуже, возвращает something помимо поля
Enum
, которого вы ищете (это - другое серьезное основание использовать
все-заглавные имена полей):
>>> class FieldTypes(Enum):
... name = 0
... value = 1
... size = 2
...
>>> FieldTypes.value.size
<FieldTypes.size: 2>
>>> FieldTypes.size.value
2
Изменено в версии 3.5.
Булев значение классов Enum
и полей¶
Enum
поля, смешанные с типами не Enum
(например, int
,
str
и т.д.), оцениваются в соответствии с правилами смешанного типа; в
противном случае все поля вычисляются как True
. Чтобы собственная
логическая оценка Enum зависела от значение участника, добавьте в класс
следующее:
def __bool__(self):
return bool(self.value)
Enum
классы с методами¶
Если вы даете ваши Enum
подкласс дополнительные методы, как класс
Планета выше, эти методы будут отображаться в 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
¶
Если комбинация элементов 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>