Задать вопрос
@vrykovan

Можно ли улучшить приложенный AutoEnum (см. код ниже) в python?

Здравствуйте все!
Первый раз пишу вопрос здесь, возможно его примут.

Решил попробовать сделать свой AutoEnum в Python, избавляющий от необходимости присваивать auto() каждому элементу
from enum import Enum
from enum import auto

class TestAutoEnum(Enum):
    FIRST = auto(123)
    SECOND = auto()

Получилось вот так:
@auto(start=123)
class From123AutoEnum(Enum): pass

class TestAutoEnum(From123AutoEnum):
    FIRST = ()
    SECOND = ()

assert TestAutoEnum.FIRST.value == 123
assert TestAutoEnum.SECOND.value == 124

Как можно написать, чтобы то же самое выглядело проще:
@auto(start=123)
class TestAutoEnum(Enum):
    FIRST = ()
    SECOND = ()
, т.е. чтобы не наследоваться от промежуточного класса
@auto(start=123)
class From123AutoEnum(Enum): pass
?

Моя реализация полностью:
from enum import EnumType, Enum
from typing import Callable


def _new_fn(start: int) -> Callable[[EnumType], Enum]:
    def __new__(cls: EnumType) -> Enum:
        obj = object.__new__(cls)
        obj._value_ = start + len(cls.__members__)

        return obj

    return __new__

def auto(cls: EnumType|None=None, /, *, start: int=1) -> EnumType:
    def wrap(cls: EnumType) -> Callable[[EnumType, int], EnumType]:
        def _process_class(cls: EnumType, start_value: int) -> EnumType:
            cls.__new__ = _new_fn(start_value)

            return cls

        return _process_class(cls, start)

    if cls is None:
        return wrap

    return wrap(cls)


@auto
class DefaultAutoEnum(Enum): pass


if __name__ == "__main__":
    class TestAutoEnum(DefaultAutoEnum):
        FIRST = ()
        SECOND = ()

    assert TestAutoEnum.FIRST.value == 1
    assert TestAutoEnum.SECOND.value == 2


    @auto(start=123)
    class From123AutoEnum(Enum): pass

    class TestAutoEnum(From123AutoEnum):
        FIRST = ()
        SECOND = ()

    assert TestAutoEnum.FIRST.value == 123
    assert TestAutoEnum.SECOND.value == 124

Так как декоратор (у меня @auto) применяется к уже созданному объекту,
то элементы перечисления создаются раньше, чем __new__ из декоратора может повлиять на их создание.

Отсюда предположение, что для задуманного может потребоваться глубокое изменение в Enum или метаклассе EnumType.

Буду признателен за любые мысли по этому поводу, в какую сторону смотреть. Может быть у кого-то уже был опыт в чём-то подобном.

Спасибо.
  • Вопрос задан
  • 140 просмотров
Подписаться 1 Средний Комментировать
Пригласить эксперта
Ответы на вопрос 1
Vindicar
@Vindicar
RTFM!
Очевидно, что декоратор должен находить и заменять объявленные константы сам, так как всё остальное уже отработало.
Если хочешь полагаться на метакласс, ему можно передавать аргументы при наследовании. ЕМНИП:
class MyMeta(type):
    def __new__(metacls, name, bases, namespace, **kwargs):
        print(f"__new__(): {name}({kwargs})")
        return super().__new__(metacls, name, bases, namespace)

class MyBase(metaclass=MyMeta):  # __new__(): MyBase({})
    pass

class MyClass(MyBase, foo='bar'):  # __new__(): MyClass({'foo': 'bar'})
    pass

А вообще я не вижу выигрыша в твоём классе. Статическая типизация, к слову, барахлить не начнёт? Среда разработки будет правильно опознавать TestAutoEnum.FIRST как экземпляр TestAutoEnum?
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы