Enum
Enum определяет перечислимый тип данных с возможностями итерации и сравнения элементов. Позволяет создавать символические имена для элементов (альтернатива строковым литералам словарей и индексам списков)
import enum
class BugStatus(enum.Enum):
new: int = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
print('Member name: {}'.format(BugStatus.wont_fix.name))
>>> Member name: wont_fix
print('Member value: {}'.format(BugStatus.wont_fix.value))
>>> Member value: 4
print(BugStatus(1))
>>> Member value: 7
При этом элементы преобразуются в экземпляры классов, которые имеют свойство name
и value
По Enum
можно итерировать. Элементы будут предоставлены в порядке, в котором они объявлены в классе
for status in BugStatus:
print(status.name, status.value)
Так как элементы неупорядочены, то доступно только сравнение ==
или is
. При попытке сравнить больше/меньше будет выброшено исключение TypeError
. Чтобы сравнить элементы или использовать другое поведение, эквивалентное поведению “как числа”, надо использовать IntEnum
class Shape(enum.IntEnum):
circle: int = 1
squire: int = 2
print(['a', 'b', 'c'][Shape.circle])
>>> b
Элементы перечисления, которым соответствют одинаковые значения, ведут себя как алиасные ссылки на этот объект - повторяющиеся объекты в итератор Enum не добавляются. Каноническим является то имя, которое было первым связано со значением.
Уникальность значений можно “требовать” с помощью декоратора @enumunic
- в случае нахождения неуникальных значений будет поднято исключение ValueError
@enum.unique
class Shape(enum.IntEnum):
circle: int = 1
squire: int = 2
triangle: int = 3
Перечисление можно создавать программным способом. Доступен также функциональный апи
Enum(value='NewEnumName', names=<...>, *, module='...',
qualname='...', type=<mixed-in class>, start=1)
# names API
'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
доступно создание собственных методлов, включая __init__
Производные
Flag и IntFlag
Может быть скомбинирован с побитовыми операторами (&, | , ^, ~). IntFlag позволяет оперировать как с числами. |
class Color(enum.Flag):
RED = enum.auto()
BLUE = enum.auto()
GREEN = enum.auto()
WHITE = RED | BLUE | GREEN
print(Color.WHITE.value)
>>> 7
В предыдущем примере реализован auto()
обеспечивающий автоматическое присваивание номеров.
class Perm(enum.IntFlag):
R = 4
W = 2
X = 1
RW = Perm.R | Perm.W
print(Perm.R in RW)
>>> True
Для большинства нового кода настоятельно рекомендуются Enum
и Flag
, поскольку IntEnum
и IntFlag
нарушают некоторые семантические соглашения перечислений (будучи сопоставимыми с целыми числами). IntEnum
и IntFlag
следует использовать только в тех случаях, когда Enum
и Flag
не подходят; например, когда целочисленные константы заменяются перечислениями, или для взаимодействия с другими объектами.
В целом значения Enum
не ограничены числами, а сам класс может включать любые методы.
class BugStatus(enum.Enum):
new = (7, ['incomplete',
'invalid',
'wont_fix',
'in_progress'])
incomplete = (6, ['new', 'wont_fix'])
invalid = (5, ['new'])
wont_fix = (4, ['new'])
in_progress = (3, ['new', 'fix_committed'])
fix_committed = (2, ['in_progress', 'fix_released'])
fix_released = (1, ['new'])
def __init__(self, num, transitions):
self.num = num
self.transitions = transitions
def can_transition(self, new_state):
return new_state.name in self.transitions
print(BugStatus.in_progress)
>>> BugStatus.in_progress
print('Value:', BugStatus.in_progress.value)
>>> Value: (3, ['new', 'fix_committed'])
print('Custom attribute:', BugStatus.in_progress.transitions)
>>> Custom attribute: ['new', 'fix_committed']
print('Using attribute:',
BugStatus.in_progress.can_transition(BugStatus.new))
>>> Using attribute: True
How do I test if int value exists in Python Enum without using try/catch?
from enum import Enum
class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6
5 in Fruit._value2member_map_ # True
7 in Fruit._value2member_map_ # False
'Apple' in Fruit._member_names_ # True
'Mango' in Fruit._member_names_ # False
Enum HOWTO
>>> from enum import Enum
>>> class Weekday(Enum):
... MONDAY = 1
... TUESDAY = 2
... WEDNESDAY = 3
... THURSDAY = 4
... FRIDAY = 5
... SATURDAY = 6
... SUNDAY = 7
... @classmethod
... def from_date(cls, date):
... return cls(date.isoweekday())
>>> Weekday(3)
<Weekday.WEDNESDAY: 3>
>>> Weekday['MONDAY']
<Weekday.WEDNESDAY: 3>
>>> print(Weekday.THURSDAY)
Weekday.THURSDAY
>>> type(Weekday.MONDAY)
<enum 'Weekday'>
>>> isinstance(Weekday.FRIDAY, Weekday)
True
>>> print(Weekday.TUESDAY.name)
TUESDAY
>>> Weekday.WEDNESDAY.value
3
>>> from datetime import date
>>> Weekday.from_date(date.today())
<Weekday.TUESDAY: 2>
>>> for i in Weekday:
... print(i)
В случаях, когда фактические значения вхождений не имеют значения, вы можете сэкономить немного времени и использовать auto() для значений. Flag поддерживает побитовые операторы & (AND), | (OR), ^ (XOR), and ~ (INVERT); результаты этих операторов являются членами перечисления. Использование auto с флагом приводит к получению целых чисел, являющихся степенью двойки, начиная с 1. |
>>> from enum import auto
>>> class Weekday(Flag):
... MONDAY = auto()
... TUESDAY = auto()
... WEDNESDAY = auto()
... THURSDAY = auto()
... FRIDAY = auto()
... SATURDAY = auto()
... SUNDAY = auto()
... WEEKEND = SATURDAY | SUNDAY
>>> print(Weekday.WEEKEND.value)
96
Поведение auto() может быть переопределено. Для этого метод 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()
>>> [member.value for member in Ordinal]
['NORTH', 'SOUTH', 'EAST', 'WEST']
Наличие двух элементов перечисления с одинаковым именем недопустимо.
>>> class Shape(Enum):
... SQUARE = 2
... SQUARE = 3
Traceback (most recent call last):
...
TypeError: 'SQUARE' already defined as 2
Однако член перечисления может иметь другие связанные с ним имена… если не установлено, что членство уникально с помощью unique()
from enum import Enum, unique
>>> 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>
>>> @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
Специальный атрибут __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>)
Доступно сравнение на идентичность и эквивалентность
>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True
>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == 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'
IntEnum аналогичен Enum, но его члены также являются целыми числами и могут использоваться везде, где могут использоваться целые числа. Если с элементом IntEnum выполняется какая-либо целочисленная операция, результирующее значение теряет свой статус перечисления.
>>> from enum import IntEnum
>>> class Numbers(IntEnum):
... ONE = 1
... TWO = 2
... THREE = 3
>>> Numbers.THREE
<Numbers.THREE: 3>
>>> Numbers.ONE + Numbers.TWO
3
>>> Numbers.THREE + 5
8
>>> Numbers.THREE == 3
True
>>> Numbers.ONE > Numbers.TWO
False
Перечисления — это классы Python, которые, как обычно, могут иметь методы и специальные методы. Перечисления могут иметь сабклассы, только если не содержат членов. Перечисления могут быть сериализованы и десериализованы.
Кроме IntEnum доступны StrEnum, IntFlag. Другие типы можно конструировать миксуя:
>>> from enum import Enum
>>> class FloatNumbers(float, Enum):
... pass
При этом:
- При создании подкласса Enum смешанные типы должны стоять перед самим Enum в последовательности оснований.
- Смешанные типы должны иметь разрешение на подклассы. Например, bool и range не могут иметь подклассы и вызовут ошибку во время создания Enum, если они используются в качестве типа микширования.
- Хотя Enum может иметь элементы любого типа, после добавления дополнительного типа все члены должны иметь значения этого типа. Это ограничение не распространяется на примеси, которые только добавляют методы и не указывают другой тип.
- Когда вмешивается другой тип данных, атрибут value не совпадает с самим членом перечисления, хотя он и будет сравниваться с эквивалентным.
- форматирование в стиле %:
%s
и%r
вызывают__str__()
и__repr__()
класса Enum соответственно; другие коды (например,%i
или%h
дляIntEnum
) рассматривают член перечисления как смешанный тип. - Форматированные строковые литералы,
str.format()
иformat()
будут использовать метод перечисления__str__()
.
Смотри еще: