蟒蛇接口
implements的Python项目详细描述
使用decorators的pythonic接口
优点
- Favor composition over inheritance
- 从多个类继承可能会有问题,特别是当超类具有相同的方法名但不同的签名时。如果发生这种情况以确保契约的完整性,则实现将抛出一个描述性错误。
- 在导入时计算装饰器。当一个对象被实例化或一个方法被调用时,任何错误都会被引发。
- 比较干净。使用decorators可以清楚地表明我们想要共享行为。此外,不允许重命名参数。
- 代码库很小:你可以把文件复制过来这种回购存在更多的测试覆盖率。
用法
fromimplementsimportInterface,implementsclassDuck:def__init__(self,age):self.age=ageclassFlyable(Interface):@staticmethoddefmigrate(direction):passdeffly(self)->str:passclassQuackable(Interface):deffly(self)->bool:passdefquack(self):pass@implements(Flyable)@implements(Quackable)classMallardDuck(Duck):def__init__(self,age):super(MallardDuck,self).__init__(age)defmigrate(self,dir):returnTruedeffly(self):pass
上述操作将引发以下错误:
NotImplementedError:'MallardDuck'mustimplementmethod'fly((self) -> bool)'definedininterface'Quackable'NotImplementedError:'MallardDuck'mustimplementmethod'quack((self))'definedininterface'Quackable'NotImplementedError:'MallardDuck'mustimplementmethod'migrate((direction))'definedininterface'Flyable'
您可以在example.py和tests.py中找到更详细的示例。
对齐
目前有两种惯用的方法重写上述示例
第一种方法是用MIXIN写基类,在每种方法中增加^ {TT3}$。
classDuck:def__init__(self,age):self.age=ageclassFlyable:@staticmethoddefmigrate(direction):raiseNotImplementedError("Flyable is an abstract class")deffly(self)->str:raiseNotImplementedError("Flyable is an abstract class")classQuackable:deffly(self)->bool:raiseNotImplementedError("Quackable is an abstract class")defquack(self):raiseNotImplementedError("Quackable is an abstract class")classMallardDuck(Duck,Quackable,Flyable):def__init__(self,age):super(MallardDuck,self).__init__(age)defmigrate(self,dir):returnTruedeffly(self):pass
但以这种方式实现它有几个缺点:
- 当调用quack时,我们只会得到一个NotImplementedError,这可能在运行时的稍后时间发生而且,到处筹集NotImplementedError看起来很笨拙。
- 如果不检查调用super的每个父类,就不清楚了。
- 类似地,Flyable和Quackable中的fly的返回类型也不同。不熟悉python的人必须阅读Method Resolution Order。
- MallardDuck的编写器使方法migrate成为实例方法,并将参数重命名为dir,这令人困惑
- 我们真的想区分行为和遗传。
使用implements的好处是它看起来更干净,并且您将在导入时而不是在实际调用方法时获得错误
另一种方法是使用内置abc模块中的抽象基类:
fromabcimportABCMeta,abstractmethod,abstractstaticmethodclassDuck(metaclass=ABCMeta):def__init__(self,age):self.age=ageclassFlyable(metaclass=ABCMeta):@abstractstaticmethoddefmigrate(direction):pass@abstractmethoddeffly(self)->str:passclassQuackable(metaclass=ABCMeta):@abstractmethoddeffly(self)->bool:pass@abstractmethoddefquack(self):passclassMallardDuck(Duck,Quackable,Flyable):def__init__(self,age):super(MallardDuck,self).__init__(age)defmigrate(self,dir):returnTruedeffly(self):pass
使用抽象基类的好处是可以更早地抛出错误 在实例化时,如果方法没有实现;还有静态分析 如果两个方法具有不同的签名,则发出警告的工具。但这解决不了 问题2-4和实现将在导入中更早地抛出错误。 在我看来,这也不像是Python。
学分
实现的灵感来自于@elifer的一个PR。
测试
运行单元测试:
make test
运行皮棉:
make lint
运行毒性:
make test-all
许可证
麻省理工学院