Python抽象基类可以强制函数签名吗?
假设我定义了一个如下的Python抽象基类可以强制函数签名吗?,python,abstract-base-class,Python,Abstract Base Class,假设我定义了一个如下的 from abc import abstractmethod, ABCMeta class Quacker(object): __metaclass__ = ABCMeta @abstractmethod def quack(self): return "Quack!" 这确保从quack派生的任何类都必须实现quack方法。但如果我定义如下: class PoliteDuck(Quacker): def quack(self, name)
from abc import abstractmethod, ABCMeta
class Quacker(object):
__metaclass__ = ABCMeta
@abstractmethod
def quack(self):
return "Quack!"
这确保从quack
派生的任何类都必须实现quack
方法。但如果我定义如下:
class PoliteDuck(Quacker):
def quack(self, name):
return "Quack quack %s!" % name
d = PoliteDuck() # no error
我可以实例化这个类,因为我提供了quack
方法,但是函数签名不匹配。我可以看出这在某些情况下是多么有用,但我感兴趣的是确保我可以明确地调用抽象方法。如果函数签名不同,则此操作可能失败
那么:如何强制执行匹配函数签名?如果签名不匹配,我会在创建对象时遇到错误,就像我根本没有定义它一样
我知道这不是惯用语言,如果我想得到这些保证,Python是错误的语言,但这不是重点——可能吗?我建议您看看pylint。我通过静态分析运行了这段代码,在定义quack()方法的那一行,它报告:
Argument number differs from overridden method (arguments-differ)
)比你想象的还要糟糕。抽象方法仅按名称跟踪,因此您甚至不必为了实例化子类而将
quack
作为方法
class SurrealDuck(Quacker):
quack = 3
d = SurrealDuck()
print d.quack # Shows 3
系统中没有任何东西强制要求quack
甚至是一个可调用的对象,更不用说其参数与抽象方法的原始参数匹配的对象了。充其量,您可以为ABCMeta创建子类ABCMeta
,并自己添加代码,以将子类中的类型签名与父类中的原始类型签名进行比较,但这对于实现来说并不重要
(目前,将某个对象标记为“抽象”实质上只是将名称添加到父对象(
Quacker.\uuuAbstractMethods.\uuuuu
)中的冻结集属性中。使类可实例化与将该属性设置为空iterable一样简单,这对测试非常有用。)我认为这在基本的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
仍然是一项正在进行的工作,但在这种情况下它确实有效。有些情况下,某些签名变体可能不会被捕获,但在其他情况下似乎适用于大多数实际应用程序 作为记录,PyCharm IDE有一个检查,当基本签名不匹配时会发出警告:2020年这个Q&A仍然准确吗?