来源:http://wiki.woodpecker.org.cn/moin/PyTwisted/HighLevelTwisted
1. 工具的使用(Using the utilities)
1.1. Application
Twisted程序通常和 twisted.application.service.Application协作。这个类通常管理着一个运行中的服务器的所有持久化配置信息 —— 需要绑定的端口、必须被保持或者被尝试的连接位置、需要周期性完成的动作,以及几乎所有的一切。它是服务树中的根对象,实现了IService接口。
Twisted programs usually work with twisted.application.service.Application. This class usually holds all persistent configuration of a running server -- ports to bind to, places where connections to must be kept or attempted, periodic actions to do and almost everything else. It is the root object in a tree of services implementing IService.
其他的HOWTO们描述了如何为一个应用编写客户代码,但是本节描述的是如何使用已经写好的代码(这些代码可能是Twisted的一部分,也可能来自一个第三方的Twisted插件开发者)。Twisted的发布中包含了用户建立和维护Application所需的各种各样的工具。
Other HOWTOs describe how to write custom code for Applications, but this one describes how to use already written code (which can be part of Twisted or from a third-party Twisted plugin developer). The Twisted distribution comes with an assortment of tools to create and manipulate ApplicationS.
Application是一种Python对象,可以像其他对象一样被建立和维护。特别的一点是,它们可以被序列化到文件中。Twisted支持几种序列化格式。
ApplicationS are just Python objects, which can be created and manipulated in the same ways as any other object. In particular, they can be serialized to files. Twisted supports several serialization formats.
1.2. 序列化(Serialization)
*
TAP Twisted Application Pickle。这种格式由本地的Python pickle包提供支持。这种格式虽然人类看不懂,但是存取是最快的。 A Twisted Application Pickle. This format is supported by the native Python pickle support. While not being human readable, this format is the fastest to load and save.
*
TAX Twisted 包含 twisted.persisted.marmalade, 一个支持对遵循XML标准的一种格式进行序列化和解序列化的模块。这种格式是人类可读可编辑的。 Twisted contains twisted.persisted.marmalade, a module that supports serializing and deserializing from a format which follows the XML standard. This format is human readable and editable.
*
TAS Twisted 包含 twisted.persisted.aot,一个支持序列化为Python代码的模块。它的好处在于使用了Python自身的语法分析器,并且以后可以手工将这段代码加入到文件中去。 Twisted contains twisted.persisted.aot, a module that supports serializing into Python source. This has the advantage of using Python's own parser and being able to later manually add Python code to the file.
1.3. mktap和tapconvert(mktap and tapconvert)
mktap(1)工具是建立TAP(或者TAX或者TAS)文件的主要途径。它可以用来建立所有主流Twisted服务器类型——比如web、ftp或IRC——的应用。它同时也支持插件,因此当你安装一个Twisted插件(其实就是将它解压到你PYTHONPATH所指定的目录中)它会自动检测并在所有支持该插件的 Twisted应用中使用它。它可以生成上述的任何一种应用格式。
The mktap(1) utility is the main way to create a TAP (or TAX or TAS) file. It can be used to create an Application for all of the major Twisted server types like web, ftp or IRC. It also supports plugins, so when you install a Twisted plugin (that is, unpack it into a directory on your PYTHONPATH) it will automatically detect it and use any Twisted Application support in it. It can create any of the above Application formats.
想了解哪些服务器类型是可用的,使用 mktap --help。mktap --help <名称> 显示一个给定服务器的可能的配置选项。mktap支持一些配置应用所需的通用选项——要了解完整的细节,可以查看man page。
In order to see which server types are available, use mktap --help. For a given server, mktap --help <name> shows the possible configuration options. mktap supports a number of generic options to configure the application -- for full details, read the man page.
有一个重要的选项是 --append <文件名>。它用于向一个已经被序列化的Twisted应用中增加一个服务器。举例来说,它可以用来增加一个telnet服务器,使你可以通过telnet来检测和重新配置应用。
One important option is --append <filename>. This is used when there is already a Twisted application serialized to which a server should be added. For example, it can be used to add a telnet server, which would let you probe and reconfigure the application by telnetting into it.
另一个有用的工具是tapconvert(1),它用于在三种应用格式之间进行转换。
Another useful utility is tapconvert(1), which converts between all three Application formats.
1.4. twistd
拥有一个表现为各种格式的Application,也许在美学角度上是令人愉悦的,但是却不能实际的导致任何事情的发生。因此,我们需要一个程序为死的应用带来生机。在UNIX系统中(并且,在有其他的选择之前,对于别的操作系统也一样),这个程序就是twistd(1)。严格的说,twistd并不是必须的——解序列化应用,得到IService组件,调用startService,当反映器(reactor)关闭时调用stopService,然后调用reactor.run()都能手工完成。然而twistd(1)提供了许多选项可以非常有效的对程序进行设置。
Having an Application in a variety of formats, aesthetically pleasing as it may be, does not actually cause anything to happen. For that, we need a program which takes a dead Application and brings life to it. For UNIX systems (and, until there are are alternatives, for other operating systems too), this program is twistd(1). Strictly speaking, twistd is not necessary -- unserializing the application, getting the IService component, calling startService, scheduling stopService when the reactor shuts down, and then calling reactor.run() could be done manually. twistd(1), however, supplies many options which are highly useful for program set up.
twistd 支持选择反映器(reactor)(更对关于reactors的信息,参见“选择反映器(Choosing a Reactor)”),向日志文件记录日志,开启守护进程以及其他功能。twistd支持上面提到的所有应用——和一个额外的。有时,直接用Python 写代码来构建一个类很方便。在doc/examples目录里有一个这样的大型例子。当使用在Python文件里直接定义一个叫做application的Application对象的方法的时候,使用 -y 选项。
twistd supports choosing a reactor (for more on reactors, see Choosing a Reactor), logging to a logfile, daemonizing and more. twistd supports all Applications mentioned above -- and an additional one. Sometimes is is convenient to write the code for building a class in straight Python. One big source of such Python files is the doc/examples directory. When a straight Python file which defines an Application object called application is used, use the -y option.
当twistd运行的时候,它把它的进程id记录在twistd.pid文件中(这可以通过命令行开关来进行配置)。为了关闭twistd进程,杀掉那个进程的pid(你通常需要 kill `cat twistd.pid`)。当这个进程被以正常形式杀掉之后,它会留下一个shutdown Application,并在这个Application的原名后面加上-shutdown。它包含一个新的,被应用更改了的配置信息。例如,关闭 web.tap会产生一个web-shutdown.tap的新文件。
When twistd runs, it records its process id in a twistd.pid file (this can be configured via a command line switch). In order to shutdown the twistd process, kill that pid (usually you would do kill cat twisted.pid). When the process is killed in an orderly fashion it will leave behind the shutdown Application which is named the same as the original file with a -shutdown added to its base name. This contains the new configuration information, as changed in the application. For example, web.tap when shutdown will have an additional file, web-shutdown.tap.
同样的,可怕的细节都在手册页中。
As always, the gory details are in the manual page.
1.5. tap2deb
Twisted 为那些想在Debian上部署基于Twisted服务器的应用的开发者们提供了 tap2deb 程序。这个程序将Twisted应用文件(的任意一种格式——Python、源代码、xml或pickle)封装成一个Debian包,包括正确的安装和删除脚本,以及init.d脚本。这将安装者从人工停止和启动服务的工作中解放出来,并且保证了它在启动和关闭时正确的遵循init级别。 For Twisted-based server application developers who want to deploy on Debian, Twisted supplies the tap2deb program. This program wraps a Twisted Application file (of any of the supported formats -- Python, source, xml or pickle) in a Debian package, including correct installation and removal scripts and init.d scripts. This frees the installer from manually stopping or starting the service, and will make sure it goes properly up on startup and down on shutdown and that it obeys the init levels.
tap2deb 同样也可以为那些Debian的高级用户产生源码包,允许她修改和改进那些自动软件不能检测的东西(比如和虚拟包的依赖或关联)。另外,Twisted小组自己也打算出品一些常见服务的Debian包,比如Web服务器和inetd的替代品。那些包将会使全世界的精英们感到高兴——无论是依赖 tap2deb产生的,还是Debian维护者的精美手工维护工具,都可以保证和Debian的完美集成。
For the more savvy Debian users, the tap2deb also generates the source package, allowing her to modify and polish things which automated software cannot detect (such as dependencies or relationships to virtual packages). In addition, the Twisted team itself intends to produce Debian packages for some common services, such as web servers and an inetd replacement. Those packages will enjoy the best of all worlds -- both the consistency which comes from being based on the tap2deb and the delicate manual tweaking of a Debian maintainer, insuring perfect integration with Debian.
目前,在Moshe的文档里有一个Web服务器的beta版Debian档案。
Right now, there is a beta Debian archive of a web server available at Moshe's archive.
1.6. tap2rpm
tap2rpm类似于tap2deb,只是它产生的是用于Redhat以及其他相关平台的RPM包。
tap2rpm is similar to tap2deb, except that it generates RPMs for Redhat and other related platforms.
2. Twisted组件: 接口和适配器 (Twisted Components: Interfaces and Adapters)
本节翻译由Jerry Marx完成,令狐虫排版
2.1. twisted.python.components: Twisted的接口和组件的实现 (twisted.python.components: Twisted's implementation of Interfaces and Components)
2.1.1. 组件和继承Components and Inheritance
面向对象的编程语言允许程序员以创建已有类子类对象的方式来重用已有的代码。一个类子类化其他类,我们也称之为继承她的所有行为。子类可以重写 (override)或者扩展超类的行为。继承在很多情况下是非常有用的,因为继承使用起来太方便了,以至于在一些大的软件系统中被滥用,涉及到多继承的时候尤其如此。对于这样的问题有一个解决方法就是在合适的地方用委托(delegation)代替继承。委托只是简单的要求另一个对象执行委托对象的任务。因为委托涉及一些互动的小组件,这种模式通常被称为组件模式 (components)。为了支持这种设计模式。Zope 3小组创造了接口(interfaces)和适配器(adapters)。
Object oriented programming languages allow programmers to reuse portions of existing code by creating new classes of objects which subclass another class. When a class subclasses another, it is said to inherit all of its behaviour. The subclass can then override and extend the behavior provided to it by the superclass. Inheritance is very useful in many situations, but because it is so convenient to use, often becomes abused in large software systems, especially when multiple inheritance is involved. One solution is to use delegation instead of inheritance where appropriate. Delegation is simply the act of asking another object to perform a task for an object. To support this design pattern, which is often referred to as the components pattern because it involves many small interacting components, interfaces and adapters were created by the Zope 3 team.
接口只是一个简单的可以被一个对象用来说“我实现了这个接口”的标志。其它的对象就可以发出类似“请给我一个实现了为Y对象使用的X接口”的请求。为另一种对象实现一个接口的对象就称之为适配器。
Interfaces are simply markers which objects can use to say I implement this interface. Other objects may then make requests like Please give me an object which implements interface X for object type Y. Objects which implement an interface for another object type are called adapters.
超类-子类的关系是一种is-a关系。在设计对象继承层次的时候,对象模块使用子类化技术,这样就可以说子类是父类同样的类。例如:
The superclass-subclass relationship is said to be an is-a relationship. When designing object hierarchies, object modellers use subclassing when they can say that the subclass is the same class as the superclass. For example:
Toggle line numbers
1 class Shape:
2 sideLength = 0
3 def getSideLength(self):
4 return self.sideLength
5
6 def setSideLength(self, sideLength):
7 self.sideLength = sideLength
8
9 def area(self):
10 raise NotImplementedError, "Subclasses must implement area"
11
12 class Triangle(Shape):
13 def area(self):
14 return (self.sideLength * self.sideLength) / 2
15
16 class Square(Shape):
17 def area(self):
18 return self.sideLength * self.sideLength
在上面的例子中,三角形(Triangle)是一种(is-a)形状(Shape),因此Triangle类子类化了Shape类,正方形(Square)也是一种(is-a)形状(Shape),因此Square类也子类化了Shape类。
In the above example, a Triangle is-a Shape, so it subclasses Shape, and a Square is-a Shape, so it also subclasses Shape.
然而,子类化可以变的非常复杂,尤其在多继承的情况下。多继承允许一个类继承一个以上基类。过分依赖于继承的软件通常形成扩展很广层次很深的继承树,一个类继承自多个超类可能会贯穿到整个系统中。由于多继承意味着实现继承(implementation inheritance),定位一个方法的实现和确认调用正确的方法变成了一种挑战。例如:
However, subclassing can get complicated, especially when Multiple Inheritance enters the picture. Multiple Inheritance allows a class to inherit from more than one base class. Software which relies heavily on inheritance often ends up having both very wide and very deep inheritance trees, meaning that one class inherits from many superclasses spread throughout the system. Since subclassing with Multiple Inheritance means implementation inheritance, locating a method's actual implementation and ensuring the correct method is actually being invoked becomes a challenge. For example:
Toggle line numbers
1 class Area:
2 sideLength = 0
3 def getSideLength(self):
4 return self.sideLength
5
6 def setSideLength(self, sideLength):
7 self.sideLength = sideLength
8
9 def area(self):
10 raise NotImplementedError, "Subclasses must implement area"
11
12 class Color:
13 color = None
14 def setColor(self, color):
15 self.color = color
16
17 def getColor(self):
18 return self.color
19
20 class Square(Area, Color):
21 def area(self):
22 return self.sideLength * self.sideLength
程序员们喜欢使用实现继承因为这样可以使代码可读性更好,因为计算面积的代码和颜色的代码在不同的地方。这很好,有一些对象可以有颜色但是没有面积,另一些对象有面积,但是没有颜色。问题在于,正方形(Square)并不是一个面积(Area),也不是一个颜色(Color)。因此我们应该使用另外一种叫做组件的面向对象技术,它使用委托而不是继承来实现代码的重用。我们将继续使用这个经常在各种练习中用到的多继承的例子。
The reason programmers like using implementation inheritance is because it makes code easier to read since the implementation details of Area are in a separate place than the implementation details of Color. This is nice, because conceivably an object could have a color but not an area, or an area but not a color. The problem, though, is that Square is not really an Area or a Color, but has an area and color. Thus, we should really be using another object oriented technique called composition, which relies on delegation rather than inheritance to break code into small reusable chunks. Let us continue with the Multiple Inheritance example, though, because it is often used in practice.
如果Color和Area这两个基类定义了同名的方法-比方说叫做calculate-会怎么样呢?在哪里实现它呢?Square().calculate()函数实现的地方依赖于方法推演顺序(MRO: method resolution order),并且也许会被程序员重构其它看似不相关的系统中的其他代码而影响,产生晦涩的bug。我们首先想到的也许是将calculate这个函数名改为calculateArea和calculateColor来避免命名冲突。显然这样的改变会影响到整个系统的很大部分,特别是当你在整合两个不是自己写的不同的系统的时候很容易出错。
What if both the Color and the Area base class defined the same method, perhaps calculate? Where would the implementation come from? The implementation that is located for Square().calculate() depends on the method resolution order, or MRO, and can change when programmers change seemingly unrelated things by refactoring classes in other parts of the system, causing obscure bugs. Our first thought might be to change the calculate method name to avoid name clashes, to perhaps calculateArea and calculateColor. While explicit, this change could potentially require a large number of changes throughout a system, and is error-prone, especially when attempting to integrate two systems which you didn't write.
我们来看另外的例子。我们有一个电吹风(hair dryer),它使用美国的电压标准,而我们有两个插座,一个是110伏另一个是220伏(非美国标准)。如果将电吹风查到220伏的插座上就会出现一个错误,因为它期望的是110伏的电压。让电吹风去支持plug110Volt和 plug220Volt这样两个方法是很无聊的事情,那么如果我们需要把电吹风插到另一个插座上去该怎么办呢?例如:
Let's imagine another example. We have an electric appliance, say a hair dryer. The hair dryer is american voltage. We have two electric sockets, one of them an american 110 Volt socket, and one of them a foreign 220 Volt socket. If we plug the hair dryer into the 220 Volt socket, it is going to expect 110 Volt current and errors will result. Going back and changing the hair dryer to support both plug110Volt and plug220Volt methods would be tedious, and what if we decided we needed to plug the hair dryer into yet another type of socket? For example:
Toggle line numbers
1 class HairDryer:
2 def plug(self, socket):
3 if socket.voltage() == 110:
4 print "I was plugged in properly and am operating."
5 else:
6 print "I was plugged in improperly and "
7 print "now you have no hair dryer any more."
8
9 class AmericanSocket:
10 def voltage(self):
11 return 110
12
13 class ForeignSocket:
14 def voltage(self):
15 return 220
有了这些类,就可以做以下操作了:
Given these classes, the following operations can be performed:
>>> hd = HairDryer()
>>> am = AmericanSocket()
>>> hd.plug(am)
I was plugged in properly and am operating.
>>> fs = ForeignSocket()
>>> hd.plug(fs)
I was plugged in improperly and
now you have no hair dryer any more.
我们准备为ForeignSocket开发一个适配器来解决这个问题,这个适配器可以将电压转换为适合电吹风使用的电压。转换器是一个有一个单参数构造函数的类,这个参数是adaptee或者叫做original object在twisted.python.components.Adapter中有一个简单的实现,它定义了如下所示的__init__ ,你可以在希望的时候继承它。在这个例子中,我们清楚的展示了全部代码:
We are going to attempt to solve this problem by writing an Adapter for the ForeignSocket which converts the voltage for use with an American hair dryer. An Adapter is a class which is constructed with one and only one argument, the adaptee or original object. There is a simple implementation in twisted.python.components.Adapter which defines the __init__ shown below, so you can subclass it if you desire. In this example, we will show all code involved for clarity:
Toggle line numbers
1 class AdaptToAmericanSocket:
2 def __init__(self, original):
3 self.original = original
4
5 def voltage(self):
6 return self.original.voltage() / 2
现在,我们可以这样来使用它:
Now, we can use it as so:
>>> hd = HairDryer()
>>> fs = ForeignSocket()
>>> adapted = AdaptToAmericanSocket(fs)
>>> hd.plug(adapted)
I was plugged in properly and am operating.
如你所见,适配器可以改写(override)原有的实现,它也可以通过提供原有对象没有的方法扩展(extend)原有的接口。注意适配器必须显式的 (explicitly)委托任何不想改变的原有方法。否则就不能在使用原有对象的地方使用该适配器。通常这不会是问题,因为适配器通常为了是一个对象适合用于特定接口,然后就被丢弃了。
So, as you can see, an adapter can 'override' the original implementation. It can also 'extend' the interface of the original object by providing methods the original object did not have. Note that an Adapter must explicitly delegate any method calls it does not wish to modify to the original, otherwise the Adapter cannot be used in places where the original is expected. Usually this is not a problem, as an Adapter is created to conform an object to a particular interface and then discarded.
== twisted.python.components: Twisted的接口和组件的实现 (twisted.python.components: Twisted's implementation of Interfaces and Components) ==
适配器对于使用多个类来组织一个离散模块(discrete chunks)的时候是一个很有用的方法。可是在没有基础组件的时候也不是件有趣的事情。如果每段希望使用的代码都必须显示的构造一个适配器的话,组件间的耦合就太紧了。而我们想要的是一种松耦合,于是就有了twisted.python.components。
Adapters are a useful way of using multiple classes to factor code into discrete chunks. However, they are not very interesting without some more infrastructure. If each piece of code which wished to use an adapted object had to explicitly construct the adapter itself, the coupling between components would be too tight. We would like to achieve loose coupling, and this is where twisted.python.components comes in.
首先,我们需要讨论接口的更多细节。前面提到过,接口就是一个被用作标识(used as a marker)的类。接口应该是twisted.python.components.Interface的子类,Python程序员不这样用的话实在很奇怪
First, we need to discuss Interfaces in more detail. As we mentioned earlier, an Interface is nothing more than a class which is used as a marker. Interfaces should be subclasses of twisted.python.components.Interface, and have a very odd look to python programmers not used to them:
Toggle line numbers
1 from twisted.python import components
2
3 class IAmericanSocket(components.Interface):
4 def voltage(self):
5 """Return the voltage produced by this socket object, as an integer.
6 """
注意,除了继承自components.Interface之外这只是通常的类定义。可是在类定义中的只有空方法!由于Python不具有像Java那样对于接口的语言级的内建支持,这就是区别类定义和接口定义的方法。
Notice how it looks just like a regular class definition, other than inheriting from components.Interface. However, the method definitions inside the class block do not have any method body! Since Python does not have any native language-level support for Interfaces like Java does, this is what distinguishes an Interface definition from a Class.
现在我们有了一个接口的定义,我们可以使用如下的术语来讨论:类 AmericanSocket 实现了接口IAmericanSocket ,请可给我一个可用于接口IAmericanSocket 的ForeignSocket 对象的适配器。我们可以声明(declarations) 实现类实现了哪个接口,然后请求为特定对象适合某个接口的适配器。
Now that we have a defined Interface, we can talk about objects using terms like this: The AmericanSocket class implements the IAmericanSocket interface and Please give me an object which adapts ForeignSocket to the IAmericanSocket interface. We can make declarations about what interfaces a certain class implements, and we can request adapters which implement a certain interface for a specific class.
我们来看我们是如何声明一个类实现一个接口的:
Let's look at how we declare that a class implements an interface:
Toggle line numbers
1 class AmericanSocket:
2 __implements__ = (IAmericanSocket, )
3 def voltage(self):
4 return 110
声明一个类实现一个接口,只是简单的设置接口元组(tuple of interfaces)给类成员implements。在Python中圆括号括起来的后面有个逗号的变量表示一个只有一项的元组。
So, to declare that a class implements an interface, we simply set the implements class variable to a tuple of interfaces. A single item tuple in Python is created by enclosing an item in parentheses and placing a single trailing comma after it.
现在我们将AdaptToAmericanSocket改写为一个真正的适配器。我们只是简单的从components.Adapter继承然后实现接口IamericanSocket的方法。
Now, let's say we want to rewrite the AdaptToAmericanSocket class as a real adapter. We simply subclass components.Adapter and provide implementations of the methods in the IAmericanSocket interface:
Toggle line numbers
1 class AdaptToAmericanSocket(components.Adapter):
2 __implements__ = (IAmericanSocket, )
3 def voltage(self):
4 return self.original.voltage() / 2
注意在adapter中的实现声明。迄今为止,我们还没有为达成目标做任何事情。为了让这些组件变得有用,必须使用组件注册(component registry)。AdaptToAmericanSocket实现了IamericanSocket并且把电压调整为可用于对象ForeignSocket,我们可以注册AdaptToAmericanSocket为IamericanSocket 可用于ForeignSocket的适配器。直接阅读这些代码比描述它们更容易。
Notice how we placed the implements declaration on this adapter class. So far, we have not achieved anything by using components other than requiring us to type more. In order for components to be useful, we must use the component registry. Since AdaptToAmericanSocket implements IAmericanSocket and regulates the voltage of a ForeignSocket object, we can register AdaptToAmericanSocket as an IAmericanSocket adapter for the ForeignSocket class. It is easier to see how this is done in code than to describe it:
Toggle line numbers
1 from twisted.python import components
2
3 class IAmericanSocket(components.Interface):
4 def voltage(self):
5 """Return the voltage produced by this socket object, as an integer.
6 """
7
8 class AmericanSocket:
9 __implements__ = (IAmericanSocket, )
10 def voltage(self):
11 return 110
12
13 class ForeignSocket:
14 def voltage(self):
15 return 220
16
17 class AdaptToAmericanSocket(components.Adapter):
18 __implements__ = (IAmericanSocket, )
19 def voltage(self):
20 return self.original.voltage() / 2
21
22 components.registerAdapter(
23 AdaptToAmericanSocket,
24 ForeignSocket,
25 IAmericanSocket)
现在,如果在解释器中运行这段脚本,我们就可以更了解于组件。第一件要做的事情就是去检查一个对象是否实现了一个接口: Now, if we run this script in the interactive interpreter, we can discover a little more about how to use components. The first thing we can do is discover whether an object implements an interface or not:
>>> as = AmericanSocket()
>>> fs = ForeignSocket()
>>> components.implements(as, IAmericanSocket)
1
>>> components.implements(fs, IAmericanSocket)
0
如你所见,AmericanSocket 声明它实现接口IAmericanSocket, 但是 ForeignSocket 没有。如果想一起使用HairDryer和AmericanSocket,我们可以通过检查它是否实现了接口IamericanSocket来得知是否可以安全的使用。无论如何,当我们想一起使用HairDryer和ForeignSocket的时候,我们就必须首先将它适配到IAmericanSocket。我们使用接口对象来做:
As you can see, the AmericanSocket instance claims to implement IAmericanSocket, but the ForeignSocket does not. If we wanted to use the HairDryer with the AmericanSocket, we could know that it would be safe to do so by checking whether it implements IAmericanSocket. However, if we decide we want to use HairDryer with a ForeignSocket instance, we must adapt it to IAmericanSocket before doing so. We use the interface object to do this:
>>> IAmericanSocket(fs)
<__main__.AdaptToAmericanSocket instance at 0x1a5120>
以一个对象作为参数调用一个接口(的构造函数)的时候,接口就会去在适配器注册表中查找可用的适配器(为传入对象转换到该接口的适配器)。如果找到这样的适配器,就以原始对象作为参数构造一个适配器的一个实例并返回这个实例。现在HairDryer就可以和经过转换的ForeignSocket一起安全的工作了。但是,当我们试图转换一个已经实现了IamericanSocket的对象会发生什么呢?我们简单的得回原始的那个实例:
When calling an interface with an object as an argument, the interface looks in the adapter registry for an adapter which implements the interface for the given instance's class. If it finds one, it constructs an instance of the Adapter class, passing the constructor the original instance, and returns it. Now the HairDryer can safely be used with the adapted ForeignSocket. But what happens if we attempt to adapt an object which already implements IAmericanSocket? We simply get back the original instance:
>>> IAmericanSocket(as)
<__main__.AmericanSocket instance at 0x36bff0>
好了,我们可以写一个具有智能的电吹风(smartHairDryer)了,它可以自动的寻找可用的适配器:
So, we could write a new smartHairDryer which automatically looked up an adapter for the socket you tried to plug it into:
Toggle line numbers
1 class HairDryer:
2 def plug(self, socket):
3 adapted = IAmericanSocket(socket)
4 assert socket.voltage() == 110, "BOOM"
5 print "I was plugged in properly and am operating"
现在我们创建smartHairDryer的一个实例并试图把它接到各种插座上去,HairDryer会自动根据插座的类型去使用合适的适配器:
Now, if we create an instance of our new smartHairDryer and attempt to plug it in to various sockets, the HairDryer will adapt itself automatically depending on the type of socket it is plugged in to:
>>> as = AmericanSocket()
>>> fs = ForeignSocket()
>>> hd = HairDryer()
>>> hd.plug(as)
I was plugged in properly and am operating
>>> hd.plug(fs)
I was plugged in properly and am operating
瞧瞧!我们有了一个具有魔力的组件。
Voila; the magic of components.
2.2. 组件和继承 (Components and Inheritance)
如果继承了一个已经声明实现(implements)了某个接口的类,而子类又需要定义了自己的__implements__元组(因为它又实现了另外的接口),自列必须显式的包含基类的实现接口表(base clase interface list)。实现接口声明不会从继承树中得到(因为会严重影响性能),没有采用递归查找类属性__implements__的方式。
If you inherit from a class which __implements__ some interface, and your new subclass defines its own __implements__ tuple (because it implements an additional interface), you must explicitly include the base clase interface list. The Interface code does not walk the complete inheritance tree (because that would be a significant performance hit), but instead will recursively expand any tuples it finds in each class's __implements__ attribute.
举个例子,pb.Root (定义在flavors.Root) 是一个实现了接口IPBRoot的类。 这个接口表明它是一个具有远程调用方法的对象,这个远程调用方法可以使用一个新的Broker实例提供初始化对象的服务。它又一个__implements__属性:
For example, pb.Root (actually defined in flavors.Root) is a class which implements IPBRoot. This interface indicates that an object has remotely-invokable methods and can be used as the initial object served by a new Broker instance. It has an __implements__ attribute like:
Toggle line numbers
1 class Root(Referenceable):
2 __implements__ = IPBRoot,
假设你有一个实现了你自己接口IMyInterface 的类
Suppose you have your own class which implements your IMyInterface interface:
Toggle line numbers
1 class MyThing:
2 __implements__ = IMyInterface,
如果你想让这个类继承自pb.Root,你必须自己包括pb.Root接口列表。 Now if you want to make this class inherit from pb.Root, you must manually include pb.Root's interface list:
Toggle line numbers
1 class MyThing(pb.Root):
2 __implements__ = (IMyInterface, pb.Root.__implements__)