Zope 接口的用途?
-
22-09-2019 - |
题
我已经开始在我的代码中使用 Zope 接口,到目前为止,它们实际上只是文档。我使用它们来指定类应该拥有哪些属性,在适当的类中显式实现它们,并在我期望的位置显式检查它们。这很好,但如果可能的话,我希望他们做更多的事情,比如实际验证该类是否实现了该接口,而不是仅仅验证我所说的该类是否实现了该接口。我已经阅读了 zope wiki 几次,但仍然看不到接口比我目前所做的有更多用途。所以,我的问题是您还可以使用这些接口做什么,以及如何将它们用于更多用途。
解决方案
实际上,你可以,如果你的对象或类实现的接口测试。
对于您可以使用verify
模块(你通常会使用它在测试中):
>>> from zope.interface import Interface, Attribute, implements
>>> class IFoo(Interface):
... x = Attribute("The X attribute")
... y = Attribute("The Y attribute")
>>> class Foo(object):
... implements(IFoo)
... x = 1
... def __init__(self):
... self.y = 2
>>> from zope.interface.verify import verifyObject
>>> verifyObject(IFoo, Foo())
True
>>> from zope.interface.verify import verifyClass
>>> verifyClass(IFoo, Foo)
True
接口也可用于设置和测试不变量。 你可以在这里找到更多的信息:
其他提示
在我工作的地方,我们使用接口,以便我们可以使用 ZCA,或者 Zope 组件架构, ,这是一个用于制作可交换和可插入组件的完整框架 Interface
s。我们使用 ZCA,这样我们就可以应对各种方式的每个客户端定制,而不必分叉我们的软件或让所有许多每个客户端位弄乱主树。不幸的是,Zope wiki 通常非常不完整。在其网站上对 ZCA 的大部分功能都有很好但简洁的解释 ZCA 的 pypi 页面.
我不使用 Interface
s 用于检查类是否实现给定的所有方法之类的事情 Interface
. 。从理论上讲,当您向接口添加另一个方法时,这可能很有用,以检查您是否记得将新方法添加到实现该接口的所有类中。就我个人而言,我强烈喜欢创建一个新的 Interface
过度修改旧的。修改旧的 Interfaces
一旦它们位于已发布到 pypi 或组织其他部分的鸡蛋中,通常是一个非常糟糕的主意。
关于术语的快速说明:类 实施 Interface
s 和对象(类的实例) 提供 Interface
s。如果您想检查 Interface
, ,你要么写 ISomething.implementedBy(SomeClass)
或者 ISomething.providedBy(some_object)
.
那么,我们通过一些例子来说明 ZCA 的用处。让我们假设我们正在写一个博客,使用 ZCA 使其模块化。我们会有一个 BlogPost
每个帖子的对象,这将提供 IBlogPost
接口,全部定义在我们的handy-dandy中 my.blog
蛋。我们还将博客的配置存储在 BlogConfiguration
提供的对象 IBlogConfiguration
. 。以此为起点,我们可以实现新功能,而不必接触 my.blog
根本不。
以下是我们可以使用 ZCA 执行的操作示例列表,而无需更改基础 my.blog
蛋。我或我的同事已经在真正的客户项目中完成了所有这些事情(并发现它们很有用),尽管我们当时没有实施博客。:) 这里的一些用例可以通过其他方式更好地解决,例如打印 CSS 文件。
添加额外的视图(
BrowserView
s,通常注册于 ZCML 与browser:page
指令)到所有提供的对象IBlogPost
. 。我可以做一个my.blog.printable
蛋。那个鸡蛋会注册一个名为的 BrowserViewprint
为了IBlogPost
, ,它通过一个呈现博客文章 Zope 页面模板 旨在生成打印效果良好的 HTML。那BrowserView
然后会出现在 URL 处/path/to/blogpost/@@print
.Zope中的事件订阅机制。假设我想发布 RSS 提要,并且我想提前生成它们,而不是根据请求生成它们。我可以创建一个
my.blog.rss
蛋。在那个鸡蛋中,我会注册一个事件订阅者,这些事件提供 修改对象 (zope.lifecycleevent.interfaces.IObjectModified
),在提供的对象上IBlogPost
. 。每当提供的任何内容的属性发生更改时,该订阅者都会被调用IBlogPost
, ,我可以用它来更新博客文章应该出现的所有 RSS 提要。在这种情况下,最好有一个
IBlogPostModified
在每个事件结束时发送的事件BrowserView
修改博客文章,因为IObjectModified
每次属性更改时都会发送一次 - 出于性能考虑,这可能过于频繁。适配器。适配器实际上是从一个接口“转换”到另一个接口。对于编程语言极客:Zope 适配器在 Python 中实现“开放”多重调度(“开放”的意思是“您可以从任何鸡蛋中添加更多案例”),更具体的接口匹配优先于不太具体的匹配(
Interface
类可以是彼此的子类,这正是您所希望的。)适配器从一
Interface
可以用非常好的语法来调用,ISomething(object_to_adapt)
, ,或者可以通过函数查找zope.component.getAdapter
. 。多个适配器Interface
必须通过函数查找zope.component.getMultiAdapter
, ,稍微不太漂亮。对于一组给定的适配器,您可以有多个适配器
Interface
s,通过字符串区分name
您在注册适配器时提供的。该名称默认为""
. 。例如,BrowserView
实际上是适配器,它们适应它们注册的接口和 HTTPRequest 类实现的接口。您还可以查找 全部 从一个序列注册的适配器Interface
到另一个Interface
, , 使用zope.component.getAdapters( (IAdaptFrom,), IAdaptTo )
, ,它返回(名称,适配器)对的序列。这可以作为一种非常好的方式来为插件提供挂钩以将其自身附加到其上。假设我想将博客的所有帖子和配置保存为一个大的 XML 文件。我创建一个
my.blog.xmldump
鸡蛋定义了一个IXMLSegment
, ,并注册一个适配器IBlogPost
到IXMLSegment
和一个适配器IBlogConfiguration
到IXMLSegment
. 。我现在可以通过编写调用适合我想要序列化的某些对象的适配器IXMLSegment(object_to_serialize)
.我什至可以添加更多来自其他各种东西的适配器
IXMLSegment
来自鸡蛋以外的my.blog.xmldump
. 。ZCML 有一个功能,当且仅当安装了某些 Egg 时,它才可以运行特定指令。我可以用这个来拥有my.blog.rss
注册一个适配器IRSSFeed
到IXMLSegment
当且仅当my.blog.xmldump
碰巧已安装,无需制作my.blog.rss
取决于my.blog.xmldump
.Viewlet
s就像小BrowserView
您可以“订阅”页面内的特定位置。我现在不记得所有细节,但这些对于您想要出现在侧边栏中的插件等非常有用。我一时记不起它们是 Zope 还是 Plone 基地的一部分。我建议不要使用 Plone,除非您试图解决的问题实际上需要真正的 CMS,因为它是一个大而复杂的软件,而且速度往往有点慢。
你不一定真正需要
Viewlet
无论如何,因为BrowserView
可以通过在 TAL 表达式中使用 'object/@@some_browser_view' 或使用queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )
, ,但无论如何,他们都很好。标记
Interface
s。一个标记Interface
是一个Interface
不提供任何方法和属性。您可以添加标记Interface
运行时使用的任何对象ISomething.alsoProvidedBy
. 。例如,这允许您更改将在特定对象上使用哪些适配器以及哪些适配器BrowserView
s 将在其上定义。
很抱歉,我没有提供足够的细节来立即实现这些示例,但每个示例大约需要一篇博客文章。
的Zope接口可以提供去耦两段代码不应该依赖于彼此的有用方式。
说,我们有知道如何打印一个问候在模块a.py一个组件:
>>> class Greeter(object):
... def greet(self):
... print 'Hello'
和一些代码,需要以打印在问候模块b.py:
>>> Greeter().greet()
'Hello'
此布置使得难以交换出来的代码,而无需接触b.py(这可能在一个单独的包被分发)的问候手柄。取而代之的是,我们可以引入第三模块c.py其限定IGreeter接口:
>>> from zope.interface import Interface
>>> class IGreeter(Interface):
... def greet():
... """ Gives a greeting. """
现在,我们可以用它来解耦a.py和b.py.相反,实例化一个Greeter类的,现在b.py将要求公用事业提供IGreeter接口。和a.py将声明Greeter类实现该接口:
(a.py)
>>> from zope.interface import implementer
>>> from zope.component import provideUtility
>>> from c import IGreeter
>>> @implementer(IGreeter)
... class Greeter(object):
... def greet(self):
... print 'Hello'
>>> provideUtility(Greeter(), IGreeter)
(b.py)
>>> from zope.component import getUtility
>>> from c import IGreeter
>>> greeter = getUtility(IGreeter)
>>> greeter.greet()
'Hello'
我从来没有使用Zope的接口,但你可能会考虑写的元类,该上初始化检查针对该接口的类的成员,并提出了一个运行时异常如果一个方法未实现。
使用Python中,你没有其他选择。任一具有“编译”步骤,该步骤检查你的代码,或在运行时动态检查它。