Enum – один из самых сложных модулей в питоне, я не шучу. Количество нюансов – просто огромное. Так как я последние несколько дней занимаюсь улучшением поддержки
Enum в mypy, то я решил рассказать про интересные штуки из модуля enum, которые вы скорее всего могли пропустить.Доки: https://docs.python.org/3/library/enum.html Их все равно никто не читает.
global_enum
Чудовищная фича. Просто ужас. Засовывает все значения
Enum в sys.modules[module].__dict__:
>>> from enum import Enum, global_enum
>>> @global_enum
... class Pets(Enum):
... CAT = 1
... DOG = 2
>>> print(CAT)
CAT
Да, создает новые глобальные константы. Нет, mypy такое пока не поддерживает.
Для чего нужно? Синтаксический сахар для обратной совместимости. Когда были раньше
CAT и DOG как константы в модуле, а потом появляется Enum. Но все равно не советую._simple_enum
>>> from enum import IntEnum, _simple_enum
>>>
>>> @_simple_enum(IntEnum)
... class Pets:
... CAT = 1
... DOG = 2
...
>>> print(Pets.CAT)
1
Специальный внутренний хелпер для более быстрого создания
Enum классов. Используется в основном внутри CPython для ускорения импорта библиотек. Не поддерживается mypy.@uniqueДекоратор, который позволит найди дубликаты по значениям в ваших
Enum типах и вызвать ошибку. Обязателен для использования. Иначе, где-то можно сделать опечатку:
@unique # <- will find the problem
class Pets(Enum):
CAT = 1
DOG = 1 # should be 2
и искать её всю жизнь.
Flag и FlagBoundary
Зачем нужны
Flag?
from enum import IntFlag, FlagBoundary
class Permission(IntFlag, boundary=FlagBoundary.STRICT):
READ = 0
WRITE = 1
Для сочетания друг с другом:
Permission.WRITE | Permission.READ. Есть 4 разных поведения для таких случаев: https://docs.python.org/3/library/enum.html#enum.FlagBoundaryСоветую всегда использовать
FlagBoundary.STRICT, если сомневаетесь.Черная дыра для багов и изменений от версии к версии.
member и nonmember
В Enum есть довольно сложная логика, какие объекты считать за member (часть enum), какие за nonmember (просто какие-то объекты).
Кратко:
- Имя не приватное, имя не
__dunder__ и имя не _sunder_- Имя не в
_ignore_- Не вложенный класс и не метод
- Не инстанс
nonmemberПример:
from enum import Enum, member, nonmember
class Example(Enum):
_ignore_ = ['a'] # nonmember
a = 1 # nonmember
b = 2 # member
__c__ = 3 # nonmember
_d_ = 4 # nonmember
e = nonmember(5) # nonmember
__f = 6 # nonmember
def g(self): ... # nonmember
@member
def h(self): ... # member
Думаю, что всем спалось спокойнее без такого знания. Старайтесь делать такие енамы, чтобы не приходилось использовать темную магию. И старайтесь не пользоваться
member и nonmember.Enum в .pyi файлах
Недавно Typing Spec для енамов был изменен. https://typing.readthedocs.io/en/latest/spec/enums.html
Раньше в
.pyi файлах мы аннотировали енамы так:
# mymodule.pyi
class Pets(Enum):
CAT: int
DOG: int
Однако, теперь такой способ будет создавать два nonmember'а. Правильный способ:
# mymodule.pyi
class Pets(Enum):
CAT = 1
DOG = 2
Почему? Потому что значения полей – крайне важно для типа Enum.
Для старого кода mypy выкидывает ошибку.
Обсуждение: а вам нравились
Enum в Python? А сейчас?| Поддержать | YouTube | GitHub | Чат |