В python, как я могу гарантировать, что один из моих методов класса всегда вызывается, даже если подкласс отменяет его?
Например, у меня есть
class BaseHandler(object):
def prepare(self):
self.prepped = 1
Я не хочу, чтобы все, что подклассы BaseHandler, а также хотят реализовать, готовы не забыть позвонить
super(SubBaseHandler, self).prepare()
Есть ли способ гарантировать, что метод суперкласса выполняется, даже если подкласс также реализует подготовку?
Ответы
Ответ 1
Я решил эту проблему, используя метакласс.
Использование метакласса позволяет разработчику BaseHandler
быть уверенным, что все подклассы вызовут суперклассы prepare()
без настройки на любой существующий код.
Метакласс ищет реализацию prepare
для обоих классов, а затем перезаписывает подкласс, готовый к тому, который вызывает superclass.prepare
, за которым следует subclass.prepare
.
class MetaHandler(type):
def __new__(cls, name, bases, attrs):
instance = type.__new__(cls, name, bases, attrs)
super_instance = super(instance, instance)
if hasattr(super_instance, 'prepare') and hasattr(instance, 'prepare'):
super_prepare = getattr(super_instance, 'prepare')
sub_prepare = getattr(instance, 'prepare')
def new_prepare(self):
super_prepare(self)
sub_prepare(self)
setattr(instance, 'prepare', new_prepare)
return instance
class BaseHandler(object):
__metaclass__ = MetaHandler
def prepare(self):
print 'BaseHandler.prepare'
class SubHandler(BaseHandler):
def prepare(self):
print 'SubHandler.prepare'
Использование выглядит так:
>>> sh = SubHandler()
>>> sh.prepare()
BaseHandler.prepare
SubHandler.prepare
Ответ 2
Сообщите разработчикам определить prepare_hook
вместо prepare
, но
попросите пользователей вызвать prepare
:
class BaseHandler(object):
def prepare(self):
self.prepped = 1
self.prepare_hook()
def prepare_hook(self):
pass
class SubBaseHandler(BaseHandler):
def prepare_hook(self):
pass
foo = SubBaseHandler()
foo.prepare()
Если вам требуется более сложная цепочка вызовов prepare
из нескольких подклассов, то ваши разработчики должны действительно использовать super
как то, для чего он предназначался.
Ответ 3
Просто примите, что вы должны сказать людям, подклассифицирующим ваш класс, чтобы вызвать базовый метод при его переопределении. Каждое другое решение требует, чтобы вы объяснили, что они делают что-то еще, или связаны с некоторыми непифовыми хаками, которые тоже можно обойти.
Модель наследования объектов Pythons была разработана как открытая, и любая попытка пойти другим путем просто перекомплементирует проблему, которая на самом деле не существует. Просто скажите всем, используя ваши вещи, либо следовать вашим "правилам", либо программа испортится.
Ответ 4
Одно явное решение без слишком большой магии будет состоять в том, чтобы сохранить список подготовительных обратных вызовов:
class BaseHandler(object):
def __init__(self):
self.prepare_callbacks = []
def register_prepare_callback(self, callback):
self.prepare_callbacks.append(callback)
def prepare(self):
# Do BaseHandler preparation
for callback in self.prepare_callbacks:
callback()
class MyHandler(BaseHandler):
def __init__(self):
BaseHandler.__init__(self)
self.register_prepare_callback(self._prepare)
def _prepare(self):
# whatever
Ответ 5
В общем, вы можете попробовать использовать __getattribute__
, чтобы достичь чего-то подобного (до тех пор, пока кто-то не перезапишет этот метод), но это противоречит идеям Python. Существует причина для доступа к частным объектам в Python. Причина упоминается в import this