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
Purpose of Zope Interfaces?
提问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 verify
module (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:
接口还可用于设置和测试不变量。您可以在这里找到更多信息:
回答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 Interface
s. 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 组件架构,这是一个完整的框架,用于使用Interface
s制作可交换和可插拔的组件。我们使用 ZCA,这样我们就可以处理各种每个客户端的定制,而不必分叉我们的软件或让所有每个客户端的位都弄乱主树。不幸的是,Zope wiki 通常非常不完整。ZCA 的 pypi 页面上对ZCA 的大部分功能有一个很好但简洁的解释。
I don't use Interface
s 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 Interface
over modifying an old one. Modifying old Interfaces
is usually a very bad idea once they're in eggs that have been released to pypi or to the rest of your organisation.
我不会将Interface
s 用于检查类是否实现了给定Interface
. 理论上,当您向接口添加另一个方法时,这可能很有用,以检查您是否记得将新方法添加到实现该接口的所有类。我个人非常喜欢创建一个新的而Interface
不是修改一个旧的。Interfaces
一旦它们在已发布到 pypi 或您组织的其他部分的鸡蛋中,修改旧的通常是一个非常糟糕的主意。
A quick note on terminology: classes implementInterface
s, and objects (instances of classes) provideInterface
s. If you want to check for an Interface
, you would either write ISomething.implementedBy(SomeClass)
or ISomething.providedBy(some_object)
.
关于术语的快速说明:类实现Interface
s,对象(类的实例)提供Interface
s。如果要检查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 BlogPost
object for each post, which will provide an IBlogPost
interface, all defined in our handy-dandy my.blog
egg. We'll also store the blog's configuration in BlogConfiguration
objects which provide IBlogConfiguration
. Using this as a starting point, we can implement new features without necessarily having to touch my.blog
at 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.blog
egg. 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 文件。
Adding extra views (
BrowserView
s, usually registered in ZCMLwith thebrowser:page
directive) to all objects which provideIBlogPost
. I could make amy.blog.printable
egg. That egg would register a BrowserView calledprint
forIBlogPost
, which renders the blog post through a Zope Page Templatedesigned to produce HTML that prints nicely. ThatBrowserView
would then appear at the URL/path/to/blogpost/@@print
.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.rss
egg. In that egg, I'd register a subscriber for events that provide IObjectModified(zope.lifecycleevent.interfaces.IObjectModified
), on objects that provideIBlogPost
. That subscriber would get get called every time an attribute changed on anything providingIBlogPost
, 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
IBlogPostModified
event that is sent at the end of each of theBrowserView
s that modify blog posts, sinceIObjectModified
gets sent once on every single attribute change - which might be too often for performance's sake.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 (
Interface
classes can be subclasses of one another, and this does exactly what you'd hope it would do.)Adapters from one
Interface
can be called with a very nice syntax,ISomething(object_to_adapt)
, or can be looked up via the functionzope.component.getAdapter
. Adapters from multipleInterface
s have to be looked up via the functionzope.component.getMultiAdapter
, which is slightly less pretty.You can have more than one adapter for a given set of
Interface
s, differentiated by a stringname
that you provide when registering the adapter. The name defaults to""
. For example,BrowserView
s 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 ofInterface
s to anotherInterface
, usingzope.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.xmldump
egg which defines anIXMLSegment
, and registers an adapter fromIBlogPost
toIXMLSegment
and an adapter fromIBlogConfiguration
toIXMLSegment
. I can now call whichever adapter is appropriate for some object I want to serialize by writingIXMLSegment(object_to_serialize)
.I could even add more adapters from various other things to
IXMLSegment
from eggs other thanmy.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 havemy.blog.rss
register an adapter fromIRSSFeed
toIXMLSegment
iffmy.blog.xmldump
happens to be installed, without makingmy.blog.rss
depend onmy.blog.xmldump
.Viewlet
s are like littleBrowserView
s 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
Viewlet
s anyway, sinceBrowserView
s can call one another, either by using 'object/@@some_browser_view' in a TAL expression, or by usingqueryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )
, but they're pretty nice regardless.Marker
Interface
s. A markerInterface
is anInterface
that provides no methods and no attributes. You can add a markerInterface
any object at runtime usingISomething.alsoProvidedBy
. This allows you to, for example, alter which adapters will get used on a particular object and whichBrowserView
s will be defined on it.
添加额外的意见(
BrowserView
S,通常在注册ZCML与browser:page
指令),其提供的所有对象IBlogPost
。我可以做一个my.blog.printable
鸡蛋。这鸡蛋会注册一个名为BrowserView中print
的IBlogPost
,这使得通过博客文章Zope页面模板设计制作的HTML印刷品很好。这BrowserView
随后会出现在URL/path/to/blogpost/@@print
。Zope 中的事件订阅机制。假设我想发布 RSS 提要,并且我想提前而不是根据要求生成它们。我可以创造一个
my.blog.rss
鸡蛋。在那个蛋中,我会为提供IObjectModified(zope.lifecycleevent.interfaces.IObjectModified
) 的事件注册一个订阅者 ,在提供IBlogPost
. 每次提供的任何属性更改时,都会调用该订阅者IBlogPost
,我可以使用它来更新博客文章应出现的所有 RSS 提要。在这种情况下,最好
IBlogPostModified
在BrowserView
修改博客文章的每个s的末尾发送一个事件,因为IObjectModified
在每个属性更改时发送一次 - 出于性能考虑,这可能过于频繁。适配器。适配器有效地从一个接口“转换”到另一个接口。对于编程语言极客:Zope 适配器在 Python 中实现“开放”多分派(“开放”我的意思是“您可以从任何蛋中添加更多案例”),更具体的接口匹配优先于不太具体的匹配(
Interface
类可以是彼此的子类,这正是您希望它做的事情。)来自一个的适配器
Interface
可以用一种非常好的语法调用ISomething(object_to_adapt)
,或者可以通过函数查找zope.component.getAdapter
。来自多个Interface
s 的适配器必须通过函数来查找zope.component.getMultiAdapter
,这有点不那么漂亮。对于一组给定的
Interface
s,您可以有多个适配器,由name
您在注册适配器时提供的字符串区分。名称默认为""
。例如,BrowserView
s 实际上是从它们注册的接口和 HTTPRequest 类实现的接口适配的适配器。您还可以查看所有已注册的从一个序列的适配器Interface
s到另一个Interface
,使用zope.component.getAdapters( (IAdaptFrom,), IAdaptTo )
,它返回的(姓名,适配器)对的序列。这可以用作为插件提供挂钩以将其附加到的一种非常好的方式。假设我想将我所有的博客文章和配置保存为一个大的 XML 文件。我创建了一个
my.blog.xmldump
定义了的蛋IXMLSegment
,并注册了一个适配器 fromIBlogPost
toIXMLSegment
和一个适配器 fromIBlogConfiguration
toIXMLSegment
。我现在可以调用任何适合我想通过编写序列化对象的适配器IXMLSegment(object_to_serialize)
。我什至可以
IXMLSegment
从除my.blog.xmldump
. ZCML 有一个特性,当且仅当安装了一些 egg 时,它才能运行特定的指令。我可以使用它来my.blog.rss
注册一个从IRSSFeed
到IXMLSegment
iffmy.blog.xmldump
恰好安装的适配器,而不my.blog.rss
依赖于my.blog.xmldump
.Viewlet
s 就像小BrowserView
s,您可以“订阅”页面内的特定位置。我现在不记得所有的细节,但这些对于你想要出现在侧边栏中的插件非常有用。我不记得他们是基地 Zope 还是 Plone 的一部分。我建议不要使用 Plone,除非您尝试解决的问题实际上需要一个真正的 CMS,因为它是一个庞大而复杂的软件,而且速度往往有点慢。
Viewlet
无论如何,您实际上并不一定需要s,因为BrowserView
s 可以通过在 TAL 表达式中使用 'object/@@some_browser_view' 或使用 来相互调用queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )
,但无论如何它们都非常好。标记
Interface
s。标记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,您没有其他选择。要么有一个“编译”步骤来检查您的代码,要么在运行时动态检查它。