У меня есть пользовательские 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
. Между методом экземпляра метакласса и методом класса экземпляра метакласса есть сходства, но они не совпадают.