Python抽象基类可以强制函数签名吗?



假设我这样定义一个抽象基类:

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是错误的语言,但这不是重点-这可能吗?

情况比你想象的还要糟。抽象方法仅按名称跟踪,因此您甚至不必为了实例化子类而使quack成为一个方法。

class SurrealDuck(Quacker):
    quack = 3
d = SurrealDuck()
print d.quack   # Shows 3

系统中没有任何东西强制quack是一个可调用对象,更不用说它的参数与抽象方法的原始参数匹配了。在最好的情况下,您可以创建ABCMeta的子类,并自己添加代码,将子类中的类型签名与父类中的原始类型签名进行比较,但这很难实现。

(目前,将某些内容标记为"抽象"本质上只是将名称添加到父(Quacker.__abstractmethods__)中的冻结集属性中。使一个类可实例化非常简单,只需将这个属性设置为一个空的可迭代对象,这对测试很有用。

我建议你看看pylint。我通过静态分析运行这段代码,在定义quack()方法的那一行,它报告说:

Argument number differs from overridden method (arguments-differ)

(https://en.wikipedia.org/wiki/Pylint)

我认为这在基础python语言中没有改变,但我确实找到了一个可能有用的解决方案。mypy包似乎在抽象基类及其具体实现上强制了签名一致性。基本上,如果你在抽象基类上定义签名,所有具体类都必须遵循相同的签名。

这是一个将在mypy中中断的示例。代码取自mypy网站,但我为这个答案进行了改编。

第一个示例是将通过的代码。注意,eat方法的签名是相同的,mypy没有报错。

from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
    @abstractmethod
    def eat(self, food: str) -> None: pass
    @property
    @abstractmethod
    def can_walk(self) -> bool: pass
class Cat(Animal):
    def eat(self, food: str) -> None:
        pass  # Body omitted
    @property
    def can_walk(self) -> bool:
        return True

y = Cat()     # OK

让我们稍微调整一下这段代码,现在mypy抛出一个错误:

from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
    @abstractmethod
    def eat(self, food: str) -> None: pass
    @property
    @abstractmethod
    def can_walk(self) -> bool: pass
class Cat(Animal):
    def eat(self, food: str, drink: str) -> None:
        pass  # Body omitted
    @property
    def can_walk(self) -> bool:
        return True

y = Cat()     # Error

Mypy仍然是一个正在进行的工作,但它确实在这种情况下工作。在一些极端情况下,一些签名变体可能不会被捕获,但其他情况似乎适用于大多数实际应用。

相关内容

  • 没有找到相关文章

最新更新