В материале рассмотрены тонкости реализации модуля enum
в Python.
enum.Enum
.enum.Enum
.__dunder__
имен._sunder_
имен._Private__names
.Enum
.enum.Flag
и enum.IntFlag
.enum.Enum
.Новый класс Enum
должен иметь один базовый класс Enum
, до одного конкретного типа данных и столько классов примесей на основе объектов, сколько необходимо. Порядок этих базовых классов:
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
.
enum.Enum
.Перечисления enum.Enum
могут быть pickling и unpickling:
>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
# True
Применяются обычные ограничения для pickling: выбираемые перечисления должны быть определены на верхнем уровне модуля, поскольку для распаковки требуется, чтобы они были импортированы из этого модуля.
Примечание. С версией протокола pickle 4 можно легко выделить перечисления, вложенные в другие классы.
Можно изменить способ выделения/извлечения членов enum.Enum
путем определения __reduce_ex__()
в классе перечисления.
__dunder__
имен.__members__
- это упорядоченное отображение (словарь) элементов member_name:member
только для чтения. Доступно только в классе.
__new__()
, если он указан, то должен создавать и возвращать члены перечисления. Это также неплохая идея, чтобы установить _value_
соответствующим образом. Как только все члены созданы, он больше не используется.
_sunder_
имен._name_
- имя участника_value_
- значение члена; можно установить/изменить в __new__()
._missing_
- функция поиска, используемая, когда значение не найдено. Может быть отменена._ignore_
- список имен в виде списка или строки, которые не будут преобразованы в члены и будут удалены из последнего класса._order_
- используется в коде Python 2/3 для обеспечения согласованности порядка членов (атрибут класса, удаляется во время создания класса)Новое в Python 3.6: _missing_
, _order_
, _generate_next_value_
.
Новое в Python 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_: # ['RED', 'BLUE', 'GREEN'] # ['RED', 'GREEN', 'BLUE']
_Private__names
.Частные имена не преобразуются в члены перечисления, а остаются обычными атрибутами.
Изменено в Python 3.11.
Члены Enum
являются экземплярами своего класса Enum
и обычно доступны как EnumClass.member
. При определенных обстоятельствах к ним также можно получить доступ как EnumClass.member.member
, но вы никогда не должны этого делать! Так как этот поиск может завершиться неудачно или что еще хуже, вернуть что-то, кроме члена Enum
, который вы ищете. Это еще одна веская причина использовать all-uppercase
для имен членов:
>>> class FieldTypes(Enum): ... name = 0 ... value = 1 ... size = 2 ... >>> FieldTypes.value.size # <FieldTypes.size: 2> >>> FieldTypes.size.value # 2
Enum
.Члены Enum
, которые смешиваются с типами, отличными от Enum
(такими как int
, str
и т. д.), оцениваются в соответствии с правилами смешанного типа. В противном случае все члены оцениваются как True
. Чтобы сделать логическую оценку собственного Enum
зависящей от значения члена, добавьте в класс следующее:
def __bool__(self): return bool(self.value)
Классы Enum
всегда оцениваются как True
.
Если предоставить своему подклассу Enum
дополнительные методы, такие как класс Planet
(в разделе "Интересные примеры классов перечислений"), то эти методы будут отображаться в функции dir()
у членов перечисления, но не в самом классе:
>>> dir(Planet) # ['EARTH', 'JUPITER', 'MARS', # 'MERCURY', 'NEPTUNE', 'SATURN', # 'URANUS', 'VENUS', '__class__', # '__doc__', '__members__', '__module__'] >>> dir(Planet.EARTH) # ['__class__', '__doc__', '__module__', # 'name', 'surface_gravity', 'value']
enum.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.RED|GREEN|BLUE: 7>
enum.Flag
и enum.IntFlag
.Будем использовать следующий фрагмент для примеров:
class Color(IntFlag): BLACK = 0 RED = 1 GREEN = 2 BLUE = 4 PURPLE = RED | BLUE WHITE = RED | GREEN | BLUE
Для этого кода верно следующее:
во время итерации возвращаются только канонические флаги:
>>> list(Color.WHITE) # [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
отрицание флага или набора флагов возвращает новый флаг/флаг с соответствующим положительным целочисленным значением:
>>> Color.BLUE # <Color.BLUE: 4> >>> ~Color.BLUE # <Color.RED|GREEN: 3>
имена псевдофлагов строятся из имен их членов:
>>> (Color.RED | Color.GREEN).name # 'RED|GREEN'
многобитные флаги, также известные как псевдонимы, могут быть возвращены из операций:
>>> Color.RED | Color.BLUE # <Color.PURPLE: 5> >>> Color(7) # or Color(-1) # <Color.WHITE: 7> >>> Color(0) # <Color.BLACK: 0>
проверка принадлежности/содержания: флаги с нулевым значением всегда считаются содержащимися:
>>> Color.BLACK in Color.WHITE # True
в противном случае, только если все биты одного флага находятся в другом флаге, будет возвращено значение True:
>>> Color.PURPLE in Color.WHITE # True >>> Color.GREEN in Color.PURPLE # False
Существует новый пограничный механизм, который контролирует, как обрабатываются биты, выходящие за пределы допустимого диапазона/недопустимые: STRICT
, CONFORM
, EJECT
и KEEP
:
STRICT
- вызывает исключение при представлении недопустимых значений.CONFORM
- отбрасывает все недопустимые биты.EJECT
- теряет статус флага и становится обычным целым числом с заданным значением.KEEP
- сохраняет лишние биты.repr()
и str()
Значение по умолчанию для
Flag
-STRICT
, значение по умолчанию дляIntFlag
-EJECT
, а значение по умолчанию для_convert_
-KEEP
.