Zope接口的目的?

Nik*_*win 34 python zope interface zope.interface

我已经开始在我的代码中使用Zope接口,截至目前,它们实际上只是文档.我使用它们来指定类应具有的属性,在适当的类中显式实现它们,并在我期望的位置显式检查它们.这很好,但我希望他们在可能的情况下做更多的事情,比如实际验证该类是否已经实现了接口,而不仅仅是验证我已经说过该类实现了接口.我已经阅读了几次zope wiki,但仍然看不到比我目前正在做的更多的接口使用.所以,我的问题是你还可以使用这些接口,以及如何使用它们.

Ric*_*ell 52

在我工作的地方,我们使用接口,以便我们可以使用ZCA或Zope组件架构,这是一个整体框架,用于制作可交换和使用Interfaces 可插拔的组件.我们使用ZCA,以便我们可以处理所有类型的每个客户端自定义,而无需分叉我们的软件或让所有许多每个客户端的内容弄乱主树.不幸的是,Zope wiki通常很不完整.在ZCA的pypi页面上,大部分ZCA的功能都有一个很好但很简洁的解释.

我没有使用Interfaces来检查类是否实现了给定的所有方法Interface.理论上,当您向接口添加另一个方法时,这可能很有用,以检查您是否记得将新方法添加到实现该接口的所有类中.我个人更喜欢创建一个新Interface的修改旧的.Interfaces一旦他们被释放到pypi或你的组织的其他人的鸡蛋中,修改旧的通常是一个非常糟糕的主意.

关于术语的快速说明:类实现 Interface s,对象(类的实例)提供 Interface s.如果你想检查一个Interface,你会写ISomething.implementedBy(SomeClass)ISomething.providedBy(some_object).

所以,直到ZCA有用的例子.让我们假装我们正在编写一个博客,使用ZCA使其模块化.我们将为BlogPost每个帖子提供一个对象,它将提供一个IBlogPost界面,所有这些都在我们方便的花花公子my.blog鸡蛋中定义.我们还将博客的配置存储在BlogConfiguration提供的对象中IBlogConfiguration.以此为出发点,我们可以实现新功能而无需触摸my.blog.

以下列出了我们可以通过使用ZCA完成的事情示例,而无需更改基础my.blog蛋.我或我的同事已经在真正的for-client项目中完成了所有这些事情(并且发现它们很有用),尽管我们当时没有实现博客.:)这里的一些用例可以通过其他方法更好地解决,例如打印CSS文件.

  1. 将额外的视图(BrowserView通常在带有指令的ZCML中注册browser:page)添加到提供的所有对象IBlogPost.我可以做一个my.blog.printable鸡蛋.这鸡蛋会注册一个名为BrowserView中printIBlogPost,这使得通过博客文章Zope页面模板设计生成HTML,打印好听.那BrowserView将出现在URL上/path/to/blogpost/@@print.

  2. Zope中的事件订阅机制.假设我想发布RSS提要,我想提前生成它们而不是根据请求生成它们.我可以创造一个my.blog.rss鸡蛋.在那个鸡蛋中,我会为提供IObjectModified(zope.lifecycleevent.interfaces.IObjectModified)的事件注册订阅者 IBlogPost.每当某个属性在提供的任何内容上发生更改时,该订阅者就会被调用IBlogPost,我可以使用它来更新博客帖子应该出现的所有RSS提要.

    在这种情况下,最好IBlogPostModified在每个BrowserView修改博客帖子的结尾处发送一个事件,因为IObjectModified每次单个属性更改都会发送一次 - 这可能是为了性能而经常发生的.

  3. 适配器.适配器实际上是从一个接口"转换"到另一个接口.对于编程语言极客:Zope适配器在Python中实现"开放"多分派(通过"开放"我的意思是"你可以从任何蛋中添加更多案例"),更具体的接口匹配优先于特定于较少的匹配(Interface类可以是彼此的子类,这正是你希望它会做的.)

    Interface可以使用非常好的语法调用一个适配器ISomething(object_to_adapt),或者可以通过该函数查找zope.component.getAdapter.Interface必须通过该函数查找来自多个s的适配器zope.component.getMultiAdapter,这稍微不那么漂亮.

    对于给定的Interfaces 集合,您可以有多个适配器,区别name在于您在注册适配器时提供的字符串.名称默认为"".例如,BrowserViews实际上是适配器,它们可以从它们注册的接口和HTTPRequest类实现的接口进行调整.您还可以查看所有已注册的从一个序列的适配器Interfaces到另一个Interface,使用zope.component.getAdapters( (IAdaptFrom,), IAdaptTo ),它返回的(姓名,适配器)对的序列.这可以作为一种非常好的方式来为插件提供挂钩以附加自己.

    假设我想将我所有博客的帖子和配置保存为一个大的XML文件.我创建一个my.blog.xmldump蛋其限定IXMLSegment,并注册从适配器IBlogPostIXMLSegment和从适配器IBlogConfigurationIXMLSegment.我现在可以调用哪个适配器适合我想要通过编写序列化的某个对象IXMLSegment(object_to_serialize).

    我甚至可以IXMLSegment从其他各种东西中添加更多的适配器my.blog.xmldump.当且仅当安装了一些鸡蛋时,ZCML才能运行特定的指令.我可以使用它来有my.blog.rss登记从适配器IRSSFeedIXMLSegmentIFF my.blog.xmldump恰好可以安装,而不会使my.blog.rss依赖my.blog.xmldump.

  4. Viewlets就像小BrowserViews,你可以"订阅"页面内的特定位置.我现在不记得所有的细节,但是这些对于想要出现在侧边栏中的插件非常有用.

    我不记得他们是否是基础Zope或Plone的一部分.我建议不要使用Plone,除非你试图解决的问题实际上需要一个真正的CMS,因为它是一个庞大而复杂的软件,它往往有点慢.

    你不一定真的需要Viewlets,因为BrowserViews可以通过在TAL表达式中使用'object/@@ some_browser_view'或者使用来调用彼此queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' ),但是无论如何它们都非常好.

  5. 马克Interface.标记Interface是不Interface提供方法而不提供属性的标记.您可以Interface在运行时使用添加标记任何对象ISomething.alsoProvidedBy.例如,这允许您更改将在特定对象上使用哪些适配器以及BrowserView将在其上定义哪些适配器.

我很抱歉,我没有详细说明能够立即实施这些例子,但他们每个人都会大约发一篇博文.

  • +1 - 真的很遗憾没有多少人知道适配器实际上是多个调度/多方法的机制. (2认同)

Rus*_*vak 23

您实际上可以测试您的对象或类是否实现了您的接口.为此您可以使用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
Run Code Online (Sandbox Code Playgroud)

接口也可用于设置和测试不变量.您可以在这里找到更多信息:

http://www.muthukadan.net/docs/zca.html#interfaces

  • 很酷,猜猜我编写自己的测试的解决方案是不必要的.虽然拥有运行时异常而不是依赖于测试用例仍然有用,但这是一个更好的解决方案. (2认同)

Dav*_*ick 19

Zope接口可以提供一种有用的方法来解耦两个不应相互依赖的代码.

假设我们有一个知道如何在模块a.py中打印问候语的组件:

>>> class Greeter(object):
...     def greet(self):
...         print 'Hello'
Run Code Online (Sandbox Code Playgroud)

还有一些需要在模块b.py中打印问候语的代码:

>>> Greeter().greet()
'Hello'
Run Code Online (Sandbox Code Playgroud)

这种安排使得很难交换处理问候语的代码,而不会触及b.py(可能分布在单独的包中).相反,我们可以引入第三个模块c.py来定义IGreeter接口:

>>> from zope.interface import Interface
>>> class IGreeter(Interface):
...     def greet():
...         """ Gives a greeting. """
Run Code Online (Sandbox Code Playgroud)

现在我们可以用它来解耦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'
Run Code Online (Sandbox Code Playgroud)