Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python抽象基类可以强制函数签名吗?_Python_Abstract Base Class - Fatal编程技术网

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仍然准确吗?