Как я могу переопределить поведение по умолчанию для `list(MyEnum)`?

У меня есть пользовательские enum, MyEnum, в которых некоторые элементы имеют разные имена, но одинаковое значение.

from enum import Enum

class MyEnum(Enum):
    A = 1
    B = 2
    C = 3
    D = 1  # Same value as A

Следовательно, list(MyEnum) возвращает только имена некоторых членов (первое имя для каждого значения):

>>>list(MyEnum)
[<MyEnum.A: 1>, <MyEnum.B: 2>, <MyEnum.C: 3>]

Судя по всему, list(MyEnum.__members__) возвращает все имена:

>>>list(MyEnum.__members__)
['A', 'B', 'C', 'D']

Однако, если я попытаюсь переопределить метод __iter__() для моего перечисления, переопределение, похоже, не удастся:

class MyEnum(Enum):
    A = 1
    B = 2
    C = 3
    D = 1  # Same value as A

    @classmethod # an attempt to override list(MyEnum) that doesn't change anything  
    def __iter__(cls):
        return iter(list(cls.__members__)) 

Судя по всему, list(MyEnum) никогда не попадает в обычай __iter__() (на что указывает, скажем, добавление print() перед возвращением в наш кастом __iter__()).

Почему это?

Как я могу переопределить поведение list(MyEnum) по умолчанию, чтобы получить все разные имена?

Вам нужно переопределить __iter__ в подклассе Enum, а затем использовать этот подкласс для определения MyEnum. Между методом экземпляра метакласса и методом класса экземпляра метакласса есть сходства, но они не совпадают.

chepner 03.09.2024 15:32

Извините, это подкласс EnumType, тогда используйте его как метакласс для MyEnum. (Enum сам по себе является простым экземпляром EnumType, подходящим для определения перечислимых типов.)

chepner 03.09.2024 15:41

Кроме того, __iter__ должен возвращать итератор, а не просто итерируемый объект. Оно может вернуть iter(list(cls.__members__)), но не list(cls.__members__). (То есть __iter__ должен что-то вернуть с помощью метода __next__; list.__next__ не определен.)

chepner 03.09.2024 15:44

@chepner: С таким количеством комментариев ты можешь написать ответ... ;-)

Ethan Furman 03.09.2024 18:10

«Очевидно, list(MyEnum) никогда не попадает в пользовательские __iter__()», потому что вы не изменили __iter__ в типе MyEnum, вам придется сделать это в метаклассе

juanpa.arrivillaga 03.09.2024 18:40

Я уклонился от ответа, потому что не уверен, какой будет правильная реализация __iter__. (Я не знаю, каковы будут дальнейшие последствия замены использования _member_map на __members__.) Но я полагаю, что с учетом этой оговорки ответ мог бы быть подходящим.

chepner 03.09.2024 18:58

@chepner да, похоже, не существует универсального правильного ответа на вопрос, что должно возвращать переопределение. Вопрос в том, как реализовать переопределение в принципе. Ваше предложение сработало, спасибо.

the.real.gruycho 03.09.2024 19:13
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
7
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Метод класса — это не то же самое, что метод экземпляра метакласса, которым является __iter__ для Enum. Вам необходимо определить новый метакласс, который вы можете использовать для определения нового подкласса Enum, который делает то, что вы ищете.

Предостережение: я не утверждаю, что замена текущего поведения EnumType.__iter__ вашим предложением будет совместима с текущей семантикой Enum, но это сделает ваше определение доступным.

from enum import EnumType, Enum

class MyEnumType(EnumType):
    def __iter__(self):
        # Must return an Iterator, something with a __next__ method,
        # not an Iterable.
        return iter(list(self.__members__))

class MyEnumBase(Enum, metaclass=MyEnumType):
    pass

class MyEnum(MyEnumBase):
    A = 1
    B = 2
    C = 3
    D = 1

assert list(MyEnum) == ['A', 'B', 'C', 'D']

разве это не может быть просто return iter(self.__members__)?

juanpa.arrivillaga 03.09.2024 20:26

Вероятно; __members__ сам по себе является mappingproxy объектом, который можно повторять.

chepner 03.09.2024 20:47

Другие вопросы по теме