Может ли базовый класс Python Abstract Base подписывать функции?
Предположим, что я определяю абстрактный базовый класс следующим образом:
from abc import abstractmethod, ABCMeta
class Quacker(object):
__metaclass__ = ABCMeta
@abstractmethod
def quack(self):
return "Quack!"
Это гарантирует, что любой класс, полученный из Quacker
, должен реализовать метод quack
. Но если я определяю следующее:
class PoliteDuck(Quacker):
def quack(self, name):
return "Quack quack %s!" % name
d = PoliteDuck() # no error
Мне разрешено создавать экземпляр класса, потому что я предоставил метод quack
, но сигнатуры функций не совпадают. Я вижу, как это может быть полезно в некоторых ситуациях, но я заинтересован в том, чтобы я мог определенно назвать абстрактные методы. Это может привести к сбою, если сигнатура функции отличается!
Итак: как я могу применить соответствующую подпись функции? Я бы ожидал ошибку при создании объекта, если подписи не совпадают, точно так же, как если бы я не определил его вообще.
Я знаю, что это не идиоматично, и что Python - неправильный язык, который нужно использовать, если я хочу получить такие виды гарантий, но это не так, возможно ли это?
Ответы
Ответ 1
Это хуже, чем вы думаете. Абстрактные методы отслеживаются только по имени, поэтому вам даже не нужно делать метод quack
для создания экземпляра дочернего класса.
class SurrealDuck(Quacker):
quack = 3
d = SurrealDuck()
print d.quack # Shows 3
В системе нет ничего, что обеспечивало бы, что quack
является даже вызываемым объектом, не говоря уже о том, чьи аргументы соответствуют оригиналу абстрактного метода. В лучшем случае вы можете подклассифицировать ABCMeta
и добавить код самостоятельно, чтобы сравнить сигнатуры типов в дочернем по отношению к оригиналам в родительском объекте, но это было бы нетривиально для реализации.
(В настоящее время, отмечая что-то как "абстрактное", по существу просто добавляет имя в атрибут "замороженный набор" в родительском (Quacker.__abstractmethods__
). Создание класса-экземпляра так же просто, как установка этого атрибута на пустой итерабельный, что полезно для тестирования.)
Ответ 2
Я рекомендую вам посмотреть на pylint. Я пропустил этот код через статический анализ, и в строке, где вы определили метод quack(), он сообщил:
Argument number differs from overridden method (arguments-differ)
(https://en.wikipedia.org/wiki/Pylint)