#python #inheritance #overriding #metaclass
#python #наследование #переопределение #метакласс
Вопрос:
Я почти убежден, что это невозможно, но вот что: у меня есть класс a(enum.Enum), где я хотел бы переопределить тот __contains__
, который унаследован от metaclass=enum .EnumMeta Возможно ли переопределить EnumMeta::__contains__
из моего класса a?
Я думаю, мне нужно дублировать все, чтобы достичь этого?
Комментарии:
1. В некотором смысле, а затем снова.. Я бы предпочел не заменять перечисление базового класса, как предлагается, что требует от меня дублирования всего содержимого класса Enum (нежелательно). Я нашел другой способ, который позволяет мне сохранить стандартные классы без изменений, так что, если они будут изменены в будущем, я все равно получу эти изменения, и, хотя это не классическое наследование, преимущества перевешивают сложность.
Ответ №1:
Ваше решение будет работать в вашем конкретном перечислении, но, как я указал в комментарии: оно также изменит поведение всех перечислений в одном приложении, включая частные перечисления, используемые глубоко внутри любой библиотеки (и даже перечисления в stdlib).
«Чистый» способ сделать это, следуя правилам объектно-ориентированного наследования, заключается в следующем: создайте новый метакласс, наследующий от существующего метакласса for Enum
, и обновите __contains__
метод в этом классе.
Затем создайте новую Enum
базу, используя этот новый метакласс, но идентичный Enum
в других отношениях (достигается путем простой записи его с пустым телом):
import enum
class CustomEnumMeta(enum.EnumMeta):
def __contains__(cls, element):
# Mocking "contains"
# It is usual to place "cls" instead of "self" in the metaclass
# as the received "instance" will be a class in itself.
return True
class CustomEnum(Enum, metaclass=CustomEnumMeta):
pass
class MyEnum(CustomEnum):
a = 1
b = 2
И в интерактивном приглашении:
In [10]: "x" in MyEnum
Out[10]: True
In [11]: MyEnum.a
Out[11]: <MyEnum.a: 1>
Если вы хотите отложить до обычных правил сдерживания после некоторой пользовательской логики, в производном метаклассе вы можете просто вызвать super().__contains__
после того, как закончите с вашими персонализированными правилами, например, в:
...
def __contains__(cls, element):
if member in cls._member_map_:
return True
return super().__contains__(cls, element)
Он немного отличается от того, что вы сделали, но он не дублирует логику оригинала __contains__
в Enum в вашем пользовательском методе.
Ответ №2:
Итак, я узнал, что один из способов выглядит так:
def __mycontains__(cls, member):
# this part is stolen from EnumMetas __contains__
if isinstance(member, cls):
return member._name_ in cls._member_map_
if not isinstance(member, str):
raise ValueError('Illegal enum name: {}'.format(member))
return member in cls._member_map_
class a(Enum):
flaf = auto()
setattr(a.__class__, __contains__, __mycontains__)
Это изменяет вызываемую функцию при выполнении if 'flaf' in a:
по мере необходимости.
Возможно, есть случаи, когда этот подход не самый лучший, и если да, я хотел бы услышать об этом — со своей стороны, я использую это для сопоставления проанализированных текстовых кодов с перечислениями, чтобы связать вещи с типизированными значениями, а не со строками…
Комментарии:
1.Да, но этот подход задаст новое
__contains__
поведение для всех__contains__
всех перечислений в вашем приложении — даже перечислений, которые существуют внутри библиотек, о которых вы не знаете, и которые могут использоваться в процессе работы.2. Ответ в вопросе, который в настоящее время помечен как дубликат этого, также не подходит, поскольку он не учитывает существующий метакласс Enum .