接口(interface)主要进行 API 的定义。它描述了一个应该实现需要的行为的类的方法 和属性的列表。这个描述不实现任何代码,只是为希望实现接口的任何类定义了显式契约。 任何类都可以以任何想要的方式实现一个或多个接口。
虽然 Python 更喜欢使用鸭子类型,而不是显式接口定义,但有时后者可能更好。例如, 显式接口定义使框架更容易定义接口上的功能。
好处是类是松耦合的,这被认为是一个好的做法。例如,为了执行给定的过程,类 A 不依赖于类 B,而是依赖于接口 I.类 B 实现了 I,但它可以是任何其他类。
在许多静态类型的语言中都内置了对这种技术的支持,例如 Java 或 Go。接口允许函 数或方法限制实现给定接口的可接受参数对象的范围,无论它来自哪个类。这比将参数限 制到给定类型或其子类更灵活。它像鸭子类型行为的显式版本:Java 使用接口在编译时验 证类型安全性,而不是在运行时使用鸭子类型将事物绑定在一起。
与 Java 相比,Python 有一个完全不同的类型原理,所以它没有本地支持的接口。无论 如何,如果你想对应用程序接口有更明确的控制,通常有两种解决方案可供选择。
• 使用一些添加接口概念的第三方框架。
• 使用一些高级语言特性来构建处理接口的方法。 (1)使用 zope.interface
你可以使用一些框架在 Python 中构建显式接口。最值得注意的一个是 Zope 项目的一
部分。就是 zope.interface 包。虽然,现在,Zope 不像以前那么流行,zope.interface 包仍然是 Twisted 框架的主要组件之一。
zope.interface 包的核心类是 Interface 类。你可以通过子类化来显式地定义一 个新的接口。让我们假设我们要为矩形的每个实现定义强制性接口,如下所示:
from zope.interface import Interface, Attribute
class IRectangle(Interface):
width = Attribute("The width of rectangle")
height = Attribute("The height of rectangle")
def area():
""" 返回矩形的面积
"""
def perimeter(): """ 返回矩形的周长
"""
使用 zope.interface 定义接口时需要注意的一些重要事项如下。
• 接口的常用命名约定是使用 I 作为名称后缀。
• 接口的方法不能使用 self 参数。
• 由于接口不提供具体的实现,它应该只包含空方法。你可以使用 pass 语句,抛出
NotImplementedError,或提供 docstring(首选)。
• 接口还可以使用 Attribute 类指定所需的属性。 当你定义了这样的约定时,你可以定义新的具体类,为我们的 IRectangle 接口提供实
现。为了做到这一点,你需要使用 implementer()类装饰器,并实现所有定义的方法和 属性如下所示:
@implementer(IRectangle)
class Square:
""" 使用 rectangle 接口的正方形实现 """
def __init__(self, size): self.size = size
@property
def width(self):
return self.size
@property
def height(self):
return self.size
def area(self):
return self.size ** 2
def perimeter(self):
return 4 * self.size
@implementer(IRectangle)
class Rectangle:
""" 矩形的具体实现
"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return self.width * 2 + self.height * 2
通常,接口定义了具体实现需要满足的约定。此设计模式的主要优点是能够在使用对象 之前验证约定和实现之间的一致性。使用普通的鸭子类型方法,只有当运行时缺少属性或方 法时,才会发现不一致。使用 zope.interface,你可以使用 zope.interface.verify 模块中的两个方法内省实际实现,已在早期发现不一致。
• verifyClass(interface,class_object):这将验证类对象是否存在方法 以及它们的签名的正确性,无需查找属性。
• verifyObject(interface,instance):它验证方法,它们的签名以及实际 对象实例的属性。
由于我们已经定义了我们的接口和两个具体的实现,让我们在以下交互式会话中验证
它们的约定:
>>> from zope.interface.verify import verifyClass, verifyObject
>>> verifyObject(IRectangle, Square(2))
True
>>> verifyClass(IRectangle, Square)
True
>>> verifyObject(IRectangle, Rectangle(2, 2))
True
>>> verifyClass(IRectangle, Rectangle)
True
没有什么令人印象深刻。Rectangle 和 Square 类仔细地遵循定义的约定,所以除 了成功验证之外,没有什么可做的了。但是当我们犯错误时会发生什么?让我们看一个没 有提供完整的 IRectangle 接口实现的两个类的例子如下所示:
@implementer(IRectangle)
class Point:
def __init__(self, x, y): self.x = x
self.y = y
@implementer(IRectangle)
class Circle:
def __init__(self, radius): self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius