python Zope 接口的目的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2521189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-04 00:50:22  来源:igfitidea点击:

Purpose of Zope Interfaces?

pythoninterfacezopezope.interface

提问by Nikwin

I have started using Zope interfaces in my code, and as of now, they are really only documentation. I use them to specify what attributes the class should possess, explicitly implement them in the appropriate classes and explicitly check for them where I expect one. This is fine, but I would like them to do more if possible, such as actually verify that the class has implemented the interface, instead of just verifying that I have said that the class implements the interface. I have read the zope wiki a couple of times, but still cannot see much more use for interfaces than what I am currently doing. So, my question is what else can you use these interfaces for, and how do you use them for more.

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

采纳答案by Ruslan Spivak

You can actually test if your object or class implements your interface. For that you can use verifymodule (you would normally use it in your tests):

您实际上可以测试您的对象或类是否实现了您的接口。为此,您可以使用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

Interfaces can also be used for setting and testing invariants. You can find more information here:

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

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

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

回答by Richard Barrell

Where I work, we use Interfaces so that we can use ZCA, or the Zope Component Architecture, which is a whole framework for making components that are swappable and pluggable using Interfaces. We use ZCA so that we can cope with all manner of per-client customisations without necessarily having to fork our software or have all of the many per-client bits messing up the main tree. The Zope wiki is often quite incomplete, unfortunately. There's a good-but-terse explanation of most of ZCA's features on its ZCA's pypi page.

在我工作的地方,我们使用接口,以便我们可以使用 ZCA 或Zope 组件架构,这是一个完整的框架,用于使用Interfaces制作可交换和可插拔的组件。我们使用 ZCA,这样我们就可以处理各种每个客户端的定制,而不必分叉我们的软件或让所有每个客户端的位都弄乱主树。不幸的是,Zope wiki 通常非常不完整。ZCA 的 pypi 页面上对ZCA 的大部分功能有一个很好但简洁的解释。

I don't use Interfaces for anything like checking that a class implements all the methods for a given Interface. In theory, that might be useful when you add another method to an interface, to check that you've remembered to add the new method to all of the classes that implement the interface. Personally I strongly prefer to create a new Interfaceover modifying an old one. Modifying old Interfacesis usually a very bad idea once they're in eggs that have been released to pypi or to the rest of your organisation.

我不会将Interfaces 用于检查类是否实现了给定Interface. 理论上,当您向接口添加另一个方法时,这可能很有用,以检查您是否记得将新方法添加到实现该接口的所有类。我个人非常喜欢创建一个新的而Interface不是修改一个旧的。Interfaces一旦它们在已发布到 pypi 或您组织的其他部分的鸡蛋中,修改旧的通常是一个非常糟糕的主意。

A quick note on terminology: classes implementInterfaces, and objects (instances of classes) provideInterfaces. If you want to check for an Interface, you would either write ISomething.implementedBy(SomeClass)or ISomething.providedBy(some_object).

关于术语的快速说明:类实现Interfaces,对象(类的实例)提供Interfaces。如果要检查Interface,则可以编写ISomething.implementedBy(SomeClass)ISomething.providedBy(some_object)

So, down to examples of where ZCA is useful. Let's pretend that we're writing a blog, using the ZCA to make it modular. We'll have a BlogPostobject for each post, which will provide an IBlogPostinterface, all defined in our handy-dandy my.blogegg. We'll also store the blog's configuration in BlogConfigurationobjects which provide IBlogConfiguration. Using this as a starting point, we can implement new features without necessarily having to touch my.blogat all.

因此,请看 ZCA 有用的示例。让我们假设我们正在写一个博客,使用 ZCA 使其模块化。我们将为BlogPost每个帖子提供一个对象,该对象将提供一个IBlogPost界面,所有这些都在我们的方便花花公子中定义my.blog。我们还将博客的配置存储在BlogConfiguration提供IBlogConfiguration. 以此为起点,我们可以实现新功能,而无需进行任何接触my.blog

The following is a list of examples of things that we can do by using ZCA, without having to alter the base my.blogegg. I or my co-workers have done all of these things (and found them useful) on real for-client projects, though we weren't implementing blogs at the time. :) Some of the use cases here could be better solved by other means, such as a print CSS file.

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

  1. Adding extra views (BrowserViews, usually registered in ZCMLwith the browser:pagedirective) to all objects which provide IBlogPost. I could make a my.blog.printableegg. That egg would register a BrowserView called printfor IBlogPost, which renders the blog post through a Zope Page Templatedesigned to produce HTML that prints nicely. That BrowserViewwould then appear at the URL /path/to/blogpost/@@print.

  2. The event subscription mechanism in Zope. Say I want to publish RSS feeds, and I want to generate them in advance rather than on request. I could create a my.blog.rssegg. In that egg, I'd register a subscriber for events that provide IObjectModified(zope.lifecycleevent.interfaces.IObjectModified), on objects that provide IBlogPost. That subscriber would get get called every time an attribute changed on anything providing IBlogPost, and I could use it to update all the RSS feeds that the blog post should appear in.

    In this case, it might be better to have an IBlogPostModifiedevent that is sent at the end of each of the BrowserViews that modify blog posts, since IObjectModifiedgets sent once on every single attribute change - which might be too often for performance's sake.

  3. Adapters. Adapters are effectively "casts" from one Interface to another. For programming language geeks: Zope adapters implement "open" multiple-dispatch in Python (by "open" I mean "you can add more cases from any egg"), with more-specific interface matches taking priority over less-specific matches (Interfaceclasses can be subclasses of one another, and this does exactly what you'd hope it would do.)

    Adapters from one Interfacecan be called with a very nice syntax, ISomething(object_to_adapt), or can be looked up via the function zope.component.getAdapter. Adapters from multiple Interfaces have to be looked up via the function zope.component.getMultiAdapter, which is slightly less pretty.

    You can have more than one adapter for a given set of Interfaces, differentiated by a string namethat you provide when registering the adapter. The name defaults to "". For example, BrowserViews are actually adapters that adapt from the interface that they're registered on and an interface that the HTTPRequest class implements. You can also look up allof the adapters that are registered from one sequence of Interfaces to another Interface, using zope.component.getAdapters( (IAdaptFrom,), IAdaptTo ), which returns a sequence of (name, adapter) pairs. This can be used as a very nice way to provide hooks for plugins to attach themselves to.

    Say I wanted to save all my blog's posts and configuration as one big XML file. I create a my.blog.xmldumpegg which defines an IXMLSegment, and registers an adapter from IBlogPostto IXMLSegmentand an adapter from IBlogConfigurationto IXMLSegment. I can now call whichever adapter is appropriate for some object I want to serialize by writing IXMLSegment(object_to_serialize).

    I could even add more adapters from various other things to IXMLSegmentfrom eggs other than my.blog.xmldump. ZCML has a feature where it can run a particular directive if and only if some egg is installed. I could use this to have my.blog.rssregister an adapter from IRSSFeedto IXMLSegmentiff my.blog.xmldumphappens to be installed, without making my.blog.rssdepend on my.blog.xmldump.

  4. Viewlets are like little BrowserViews that you can have 'subscribe' to a particular spot inside a page. I can't remember all the details right now but these are very good for things like plugins that you want to appear in a sidebar.

    I can't remember offhand whether they're part of base Zope or Plone. I would recommend against using Plone unless the problem that you are trying to solve actually needs a real CMS, since it's a big and complicated piece of software and it tends to be kinda slow.

    You don't necessarily actually need Viewlets anyway, since BrowserViews can call one another, either by using 'object/@@some_browser_view' in a TAL expression, or by using queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' ), but they're pretty nice regardless.

  5. Marker Interfaces. A marker Interfaceis an Interfacethat provides no methods and no attributes. You can add a marker Interfaceany object at runtime using ISomething.alsoProvidedBy. This allows you to, for example, alter which adapters will get used on a particular object and which BrowserViews will be defined on it.

  1. 添加额外的意见(BrowserViewS,通常在注册ZCMLbrowser: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 提要。

    在这种情况下,最好IBlogPostModifiedBrowserView修改博客文章的每个s的末尾发送一个事件,因为IObjectModified在每个属性更改时发送一次 - 出于性能考虑,这可能过于频繁。

  3. 适配器。适配器有效地从一个接口“转换”到另一个接口。对于编程语言极客:Zope 适配器在 Python 中实现“开放”多分派(“开放”我的意思是“您可以从任何蛋中添加更多案例”),更具体的接口匹配优先于不太具体的匹配(Interface类可以是彼此的子类,这正是您希望它做的事情。)

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

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

    假设我想将我所有的博客文章和配置保存为一个大的 XML 文件。我创建了一个my.blog.xmldump定义了的蛋IXMLSegment,并注册了一个适配器 from IBlogPosttoIXMLSegment和一个适配器 from IBlogConfigurationto IXMLSegment。我现在可以调用任何适合我想通过编写序列化对象的适配器IXMLSegment(object_to_serialize)

    我什至可以IXMLSegment从除my.blog.xmldump. ZCML 有一个特性,当且仅当安装了一些 egg 时,它才能运行特定的指令。我可以使用它来my.blog.rss注册一个从IRSSFeedIXMLSegmentiffmy.blog.xmldump恰好安装的适配器,而不my.blog.rss依赖于my.blog.xmldump.

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

    我不记得他们是基地 Zope 还是 Plone 的一部分。我建议不要使用 Plone,除非您尝试解决的问题实际上需要一个真正的 CMS,因为它是一个庞大而复杂的软件,而且速度往往有点慢。

    Viewlet无论如何,您实际上并不一定需要s,因为BrowserViews 可以通过在 TAL 表达式中使用 'object/@@some_browser_view' 或使用 来相互调用queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' ),但无论如何它们都非常好。

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

I apologise that I haven't gone into enough detail to be able to implement each of these examples straight away, but they'd take approximately a blog post each.

我很抱歉我没有详细介绍这些示例中的每一个,但每个示例大约需要写一篇博文。

回答by David Glick

Zope interfaces can provide a useful way to decouple two pieces of code that shouldn't depend on each other.

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

Say we have a component that knows how to print a greeting in module a.py:

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

>>> class Greeter(object):
...     def greet(self):
...         print 'Hello'

And some code that needs to print a greeting in module b.py:

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

>>> Greeter().greet()
'Hello'

This arrangement makes it hard to swap out the code that handles the greeting without touching b.py (which might be distributed in a separate package). Instead, we could introduce a third module c.py which defines an IGreeter interface:

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

>>> from zope.interface import Interface
>>> class IGreeter(Interface):
...     def greet():
...         """ Gives a greeting. """

Now we can use this to decouple a.py and b.py. Instead of instantiating a Greeter class, b.py will now ask for a utility providing the IGreeter interface. And a.py will declare that the Greeter class implements that interface:

现在我们可以使用它来解耦 a.py 和 b.py。b.py 现在不再实例化 Greeter 类,而是要求提供一个提供 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'

回答by Daniel Naab

I've never used Zope interfaces, but you might consider writing a metaclass, which on initialization checks the members of the class against the interface, and raises a runtime exception if a method isn't implemented.

我从未使用过 Zope 接口,但您可能会考虑编写一个元类,它在初始化时根据接口检查类的成员,如果没有实现方法,则会引发运行时异常。

With Python you don't have other options. Either have a "compile" step that inspects your code, or dynamically inspect it at runtime.

使用 Python,您没有其他选择。要么有一个“编译”步骤来检查您的代码,要么在运行时动态检查它。