sim*_*nwo 21 python abstract-base-class
假设我定义了一个这样的抽象基类:
from abc import abstractmethod, ABCMeta
class Quacker(object):
__metaclass__ = ABCMeta
@abstractmethod
def quack(self):
return "Quack!"
Run Code Online (Sandbox Code Playgroud)
这确保了派生的任何类Quacker必须实现该quack方法.但是,如果我定义以下内容:
class PoliteDuck(Quacker):
def quack(self, name):
return "Quack quack %s!" % name
d = PoliteDuck() # no error
Run Code Online (Sandbox Code Playgroud)
我被允许实例化该类,因为我提供了quack方法,但函数签名不匹配.我可以看到这在某些情况下可能有用,但我有兴趣确保我可以肯定地调用抽象方法.如果功能签名不同,这可能会失败!
那么:我如何强制执行匹配的函数签名?如果签名不匹配,我会期望在创建对象时出错,就像我根本没有定义它一样.
我知道这不是惯用的,如果我想要这些类型的保证,那么Python是错误的语言,但这是不可能的 - 这是可能的吗?
che*_*ner 22
这比你想象的要糟糕.抽象方法仅按名称进行跟踪,因此您甚至不必创建quack方法来实例化子类.
class SurrealDuck(Quacker):
quack = 3
d = SurrealDuck()
print d.quack # Shows 3
Run Code Online (Sandbox Code Playgroud)
系统中没有任何东西强制执行quack甚至是可调用对象,更不用说其参数与抽象方法的原始对象匹配的对象.最好的情况是,您可以ABCMeta自己子类化并添加代码,以将子代码中的类型签名与父代中的原始代码进行比较,但这实现起来并不重要.
(目前,将某些内容标记为"abstract"实质上只是将名称添加到parent(Quacker.__abstractmethods__)中的冻结集属性.使类可实例化就像将此属性设置为空迭代一样简单,这对于测试非常有用.)
我建议你看看pylint.我通过静态分析运行此代码,并在您定义quack()方法的行上,它报告:
Argument number differs from overridden method (arguments-differ)
Run Code Online (Sandbox Code Playgroud)
(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
Run Code Online (Sandbox Code Playgroud)
但是让我们稍微调整一下这段代码,现在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
Run Code Online (Sandbox Code Playgroud)
Mypy仍然是一个正在进行的工作,但它在这种情况下确实有效。在某些极端情况下,某些签名变体可能不会被捕获,但在其他方面似乎适用于大多数实际应用程序。
| 归档时间: |
|
| 查看次数: |
4562 次 |
| 最近记录: |