Действительно, вы не должны полагаться на inspect.getsourcelines
для любого кода, который должен использоваться в серьезных контекстах (т. Е. Вне области эксперимента, или инструментов для работы с самим исходным кодом)
Простая и простаяОператора is
достаточно, чтобы проверить, является ли метод в данном классе таким же, как в базовом классе.(В Python 3. Пользователи Python 2 должны позаботиться о том, чтобы методы извлекались как unbound methods
вместо raw-функций)
Кроме этого, вы делаете несколько ненужных ходов, чтобы добраться до базового классасама по себе - мало документированная и мало используемая специальная переменная __class__
может помочь вам в этом: это автоматическая ссылка на тело класса, в котором она написана (не путайте с self.__class__
, который является ссылкойвместо этого в подкласс).
Из документации:
Это объект класса, на который будет ссылаться форма с нулевым аргументом super(). __class__
, неявнаяссылка на замыкание, созданная компилятором, если какие-либо методы в теле класса ссылаются на __class__
или super
. Это позволяет форме нулевого аргумента super()
правильно идентифицировать определяемый класс на основе лексической области видимости, тогда как класс или экземпляр, который использовался для выполнения текущего вызова, идентифицируется на основе первого аргумента, переданного вметод.
Таким образом, при сохранении вашего основного подхода все может быть гораздо проще:
def __init_subclass__(cls):
required_methods = ['on_connected', 'on_disconnected', 'on_message']
for f in required_methods:
if getattr(cls, f) is getattr(__class__, f):
raise NotImplementedError(...)
Если у вас сложная иерархия, и у вас будут родительские классы сдругие обязательные методы, которые должны быть реализованы подклассами тех, и, следовательно, не могут жестко закодировать необходимые методы в required_methods
, вы все равно можете использовать декоратор abstractmethod
из abc
, без использования ABCMeta
метаклассом.Все, что делает декоратор, - это создает атрибут метода, который проверяется в метаклассе.Просто выполните ту же проверку в методе __init_subclass__
:
from abc import abstractmethod
class Base:
def __init_subclass__(cls, **kw):
super().__init_subclass__(**kw)
for attr_name in dir(cls):
method = getattr(cls, attr_name)
if (getattr(method, '__isabstractmethod__', False) and
not attr_name in cls.__dict__):
# The second condition above allows
# abstractmethods to exist in the class where
# they are defined, but not on further subclasses
raise NotImplementedError(...)
class NetworkMixin(Base):
@abstractmethod
def on_connect(self):
pass
class FileMixin(Base):
@abstractmethod
def on_close(self):
pass
class MyFileNetworkThing(NetworkMixin, FileMixin):
# if any of the two abstract methods is not
# implemented, Base.__init_subclass__ will fail
Имейте в виду, что это просто проверка методов, которые отображаются в классе 'dir
.Но настройка __dir__
используется достаточно редко, чтобы быть надежной - просто позаботьтесь об этом.