У меня есть пользовательские 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) по умолчанию, чтобы получить все разные имена?
Извините, это подкласс EnumType, тогда используйте его как метакласс для MyEnum. (Enum сам по себе является простым экземпляром EnumType, подходящим для определения перечислимых типов.)
Кроме того, __iter__ должен возвращать итератор, а не просто итерируемый объект. Оно может вернуть iter(list(cls.__members__)), но не list(cls.__members__). (То есть __iter__ должен что-то вернуть с помощью метода __next__; list.__next__ не определен.)
@chepner: С таким количеством комментариев ты можешь написать ответ... ;-)
«Очевидно, list(MyEnum) никогда не попадает в пользовательские __iter__()», потому что вы не изменили __iter__ в типе MyEnum, вам придется сделать это в метаклассе
Я уклонился от ответа, потому что не уверен, какой будет правильная реализация __iter__. (Я не знаю, каковы будут дальнейшие последствия замены использования _member_map на __members__.) Но я полагаю, что с учетом этой оговорки ответ мог бы быть подходящим.
@chepner да, похоже, не существует универсального правильного ответа на вопрос, что должно возвращать переопределение. Вопрос в том, как реализовать переопределение в принципе. Ваше предложение сработало, спасибо.






Метод класса — это не то же самое, что метод экземпляра метакласса, которым является __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__)?
Вероятно; __members__ сам по себе является mappingproxy объектом, который можно повторять.
Вам нужно переопределить
__iter__в подклассеEnum, а затем использовать этот подкласс для определенияMyEnum. Между методом экземпляра метакласса и методом класса экземпляра метакласса есть сходства, но они не совпадают.