跳转到内容

Zope 3 手册/接口

来自维基教科书,开放世界中的开放书籍

接口是指定(文档化)“提供”它们的那些对象的外部行为的对象。 接口通过以下方式指定行为:

  • docstring 中的非正式文档
  • 属性定义
  • 不变式,即提供接口的对象必须满足的条件

使用接口的一些动机是:

  • 通过开发小型、可互换的片段来避免单块设计
  • 对外部责任、功能和行为进行建模
  • 在功能片段之间建立契约
  • 记录 API

经典软件工程书籍 设计模式(由“四人帮”编写)建议您面向接口编程,而不是面向实现编程。 定义正式接口有助于理解系统。 此外,接口为您带来了 Zope 组件架构的所有好处。

在一些现代编程语言中:Java、C#、VB.NET 等,接口是语言的显式方面。 由于 Python 缺乏接口,Zope 将它们实现为一个元类,您可以从该元类继承。

契约类型

[编辑 | 编辑源代码]
"我可以做 X"
描述做某事的能力是 API 的经典定义。 这些能力被定义并实现为方法。
"我拥有 X"
此语句声明数据的可用性,这与模式经典相关联。 数据存储在属性和特性中。
"您可以用我做 X"
这里我们描述了对象的行为。 通常没有类似物。 但是,MIME 类型是行为声明的绝佳示例。 这是使用空的“标记接口”实现的,因为它们描述了隐式行为。

这三种契约类型的区别首先由 Philipp von Weitershausen 以这种形式指出。

理解这些区别非常重要,因为其他编程语言不一定使用所有这三种概念。 事实上,通常只使用第一个概念。

定义接口

[编辑 | 编辑源代码]
  • Python 没有接口的概念
  • 不是问题
  • 接口只是对象
  • "滥用" 类语句来创建一个接口
  • PEP 245 中提出的语法

Jim Fulton 并不认为这是一个问题,因为它使接口成为一等公民。 例如,在 Java 中,接口是只能在其预期的有限范围内充当接口的特殊类型的对象。

另一方面,来自 zope.interface 包的接口通过实现元类(Python 的核心概念)来定义接口。 因此,接口只是在使用现有的 Python 模式。

一个例子

[编辑 | 编辑源代码]

这是一个经典的“你好世界”风格的例子

>>> class Host(object):
...
...     def goodmorning(self, name):
...         """Say good morning to guests"""
...
...         return "Good morning, %s!" % name

在上面的类中,您定义了一个 goodmorning 方法。 如果您从使用此类创建的对象调用 goodmorning 方法,它将返回“早上好,...”!

>>> host = Host()
>>> host.goodmorning('Jack')
'Good morning, Jack!'

这里 host 是您的代码实际使用的对象。 如果您想检查实现细节,您需要访问 Host 类,可以通过源代码或 API 文档工具进行访问。

现在我们将开始使用 Zope 接口。 对于上面给出的类,您可以像这样指定接口

>>> from zope.interface import Interface

>>> class IHost(Interface):
...
...     def goodmorning(guest):
...         """Say good morning to guest"""

如您所见,该接口继承自 zope.interface.Interface。 这种对 Python 类语句的“滥用”是 Zope 定义接口的方式。 接口名称的 I 前缀是一个有用的约定。

声明接口

[编辑 | 编辑源代码]

您已经在上一节中看到了如何使用 zope.interface 声明接口。 本节将详细解释这些概念。

考虑这个示例接口

>>> from zope.interface import Interface
>>> from zope.interface import Attribute

>>> class IHost(Interface):
...     """A host object"""
...
...     name = Attribute("""Name of host""")
...
...     def goodmorning(guest):
...         """Say good morning to guest"""

接口 IHost 有两个属性,name 和 goodmorning。 回想一下,至少在 Python 中,方法也是类的属性。 name 属性使用 zope.interface.Attribute 类定义。 当您将 name 属性添加到 IHost 接口时,您不会设置初始值。 在这里定义 name 属性的目的是仅仅表明该接口的任何实现都将具有一个名为 name 的属性。 在这种情况下,您甚至没有说明它必须是什么类型的属性! 您可以将文档字符串作为第一个参数传递给 Attribute。

另一个属性 goodmorning 是使用函数定义定义的方法。 请注意,接口中不需要 self,因为 self 是类的一个实现细节。 例如,模块可以实现此接口。 如果模块实现了此接口,将定义一个 name 属性和一个 goodmorning 函数。 并且 goodmorning 函数将接受一个参数。

现在您将看到如何连接接口-类-对象。 因此,对象是真正存在的实体,对象是类的实例。 并且接口是对象的实际定义,因此类只是实现细节。 这就是为什么您应该面向接口编程,而不是面向实现编程。

现在您应该熟悉另外两个术语来理解其他概念。 第一个是“提供”,另一个是“实现”。 对象提供接口,而类实现接口。 换句话说,对象提供了其类实现的接口。 在上面的示例中,host(对象)提供 IHost(接口),而 Host(类)实现 IHost(接口)。 一个对象可以提供多个接口; 同样,一个类可以实现多个接口。 除了其类实现的内容之外,对象也可以直接提供接口。

注意
类是对象的实现细节。 在 Python 中,类是可调用对象,那么为什么其他可调用对象不能实现接口呢? 是的,这是可能的。 对于任何可调用对象,您可以通过说明可调用对象实现了接口来声明它会生成提供某些接口的对象。 可调用对象通常被称为“工厂”。 由于函数是可调用对象,因此函数可以是接口的实现者。

实现接口

[编辑 | 编辑源代码]

要声明一个类实现特定接口,请在类语句中使用 zope.interface.implements 函数。

考虑这个例子,这里 Host 实现 IHost

>>> from zope.interface import implements

>>> class Host(object):
...
...     implements(IHost)
...
...     name = u''
...
...     def goodmorning(self, guest):
...         """Say good morning to guest"""
...
...         return "Good morning, %s!" % guest
注意
如果您想知道 implements 函数是如何工作的,请参阅 James Henstridge 的博客文章 (http://blogs.gnome.org/jamesh/2005/09/08/python-class-advisors/) 。 在适配器部分,您将看到一个 adapts 函数,它的工作原理也类似。

由于 Host 实现了 IHost,因此 Host 的实例提供 IHost。 有些实用程序方法可以内省声明。 声明也可以在类之外编写。 如果您没有在上面的示例中编写 interface.implements(IHost),那么在定义类语句之后,您可以像这样编写

>>> from zope.interface import classImplements
>>> classImplements(Host, IHost)

标记接口

[编辑 | 编辑源代码]

接口可用于声明特定对象属于特殊类型。 没有属性或方法的接口称为标记接口。

这是一个标记接口

>>> from zope.interface import Interface

>>> class ISpecialGuest(Interface):
...     """A special guest"""

此接口可用于声明对象是特邀嘉宾。

华夏公益教科书