Что мне не нравится в @absctractmethod
, так это то, что он выдает ошибку только при создании экземпляра.Например, это не приведет к ошибке:
from abc import abstractmethod, ABC
class AbstractClass(ABC):
@abstractmethod
def func(self):
pass
class RealClass(AbstractClass):
pass
произойдет сбой только в том случае, если я создам экземпляр:
r = RealClass()
Я хочу повторно реализовать этот механизм, но он не работает при определении класса, не экземпляр.Для этого я создал метакласс:
class ABCEarlyFailMeta(type):
direct_inheritors = {}
def __new__(cls, clsname, bases, clsdict):
klass = super().__new__(cls, clsname, bases, clsdict)
class_path = clsdict['__module__'] + '.' + clsdict['__qualname__']
if bases == ():
# we get here when we create base abstract class.
# The registry will later be filled with abstract methods
cls.direct_inheritors[class_path] = {}
for name, value in clsdict.items():
# adding abstract methods on the proper base abstract class
if getattr(value, '__isabstractmethod__', None) is True:
cls.direct_inheritors[class_path][name] = signature(value)
else:
# we get here when create inheritors.
# Here, we need to extract list of abstractmethods
base_class = bases[0].__module__ + '.' + bases[0].__qualname__
abstract_method_names = cls.direct_inheritors[base_class]
# here, we compare current list of methods
# with list of abstractmethods and fail if some methods are missing
cls_dictkeys = set(clsdict.keys())
missing_methods = set(abstract_method_names) - cls_dictkeys
if missing_methods:
raise Exception(
f'{clsname} must implement methods: {missing_methods}'
)
return klass
, когда класс будет создан, не будет создан экземпляр:
class ABCEarlyFail(metaclass=ABCEarlyFailMeta):
@abstractmethod
def func(self):
pass
class Child(ABCEarlyFail):
pass
>>> Exception: Child must implement methods: {'func'}
У меня вопрос, как найти правильный базовый класс в bases
?В этом примере я ищу bases[0]
, но он потерпит неудачу, если у класса-наследника есть миксин:
class Child(SomeMixin, ABCEarlyFail):
pass
Итак, что же лучше?
Или, может быть, яизобретать велосипед?