Python Twisted系列教程10:增强defer功能的客户端
作者:dave@http://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/ 译者:杨晓伟(采用意译)
可以从这里从头开始阅读这个系列。
版本5.0
现在我们将要向诗歌下载客户端添加一些新的处理逻辑,包括在第九部分提到要添加的功能。不过,首先我要说明一点:我并不知道如何实现Byronification引擎。那超出了我的编程能力范围。取而代之的,我想实现一个简单的功能,即Cummingsifier。其只是将诗歌内容转换成小写字母:
def cummingsify(poem)
return poem.lower()
这个方法如此之简单以至于它永远不会出错。版本5.0的实现代码在twisted-client-5/get-poetry.py文件中。我们使用了修改后的 cummingsify,其会随机地选择以下行为:
1.返回诗歌的小写版本
2.抛出一个GibberishError异常
3.抛出一个ValueError
这样,我们便模拟出来一个会因为各种意料不到的问题而执行失败的复杂算法。其它部分的仅有的改变在方法poetry_main中:
def poetry_main():
addresses = parse_args()
from twisted.internet import reactor
poems = []
errors = []
def try_to_cummingsify(poem):
try:
return cummingsify(poem)
except GibberishError:
raise
except:
print 'Cummingsify failed!'
return poem def got_poem(poem):
print poem
poems.append(poem) def poem_failed(err):
print >>sys.stderr, 'The poem download failed.'
errors.append(err) def poem_done(_):
if len(poems) + len(errors) == len(addresses):
reactor.stop() for address in addresses:
host, port = address
d = get_poetry(host, port)
d.addCallback(try_to_cummingsify)
d.addCallbacks(got_poem, poem_failed)
d.addBoth(poem_done) reactor.run()
因此,当从服务器上下载一首诗歌时,可能会出现如下情况:
1.打印诗歌的小写版本
2.打印 ”Cummingsify failed“并附上原始形式的诗歌
3.打印”The peom download failed”。
为了实现下面内容的效果,你可以打开多个服务器或开一个服务器而打开此程序次,直到你观察到所有不同的结果,当然也尝试一下去连接一个没有服务器值守的端口。
图19是我们给deferred添加回调后形成的callback/errback链:

图19 deferred中的回调链
注意到,"pass-throug”errback通过addCallback添加到链中。它会将任何其接收到的Failure传递给下一个errback(即poem_failed函数)。因此poem_failed函数可以处理来自get_poetry与try_to_commingsify两者的failure。下面让我们来分析下deferred可能会出现的激活情况,图20说明了我们能够下载到诗歌并且try_to_commingsify成功执行的路线图:

图20 成功下载到诗歌并且成功变换其格式
在这种情况中,没有回调执行失败,因此控制权一直在callback中流动。注意到poem_done收到的结果是None,这是因为它并没有返回任何值。如果我们想让后续的回调都能触及到诗歌内容,只要显式地让got_poem返回诗歌即可。
图21说明了我们在成功下载到诗歌后,但在try_to_cummingsify中抛出了GibberishError:

图21 成功下载到诗歌但出现了GibberishError
由于try_to_cummingsify回调抛出了GibberishError,所以控制权转移到了errback链,即poem_fail回调被调用并传入的捕获的异常作为其参数。
由于poem_failed并没有抛出获异常或返回一个Failure,因此在它执行完后,控制权又回到了callback链中。如果我们想让poem_fail完全处理好传进来的错误,那么返回一个None是再好不过的做法了。相反,如果我们只想让poem_failed采取一部分行动,但继续传递这个错误,那么我们需要改写poem_failed,即将参数err作为返回值返回。如此一来,控制权交给了下一个errback回调。
注意到,迄今为止,got_poem与poem_failed都不可能出现执行失败的情况,因此errback链上的poem_done是不可能被激活的。但在任何情况下这样做都是安全的,这体现了“防御式”编程的思想。比如在got_poem或poem_failed出现了bugs,那么这样做就不会让这个bugs的影响进入Twisted的核心代码区。鉴于上面的描述,可以看出addBoth类型于try/except中的finally语句。
下面我们再来看看第三种可能情况,即成功下载到诗歌但try_to_cummingsify抛出了VauleError,如图22:

图22:成功下载到诗歌当cummingsify执行失败
除了got_poem得到是原始式样的诗歌而不是小写版的外,与图20描述的情况完全相同。当然,控制权还是在try_to_cummingsif中进行了转移,即使用了try/except捕获了ValueError并返回了原始式样的诗歌。而这一切deferred并不知晓。
最后,我们来看看当试图连接一个无服务器值守的端口会出现什么情况,如图23所示:

图23 连接服务器失败
由于poem_failed返回了一个None,因此控权又回到了callback链中。
版本5.1
在版本5.0中我们使用普通的try/except来捕获try_to_cummingsify中的异常,而没有让deferred来捕获这个异常。这其实并没有什么错误,但下面我们将采取一种新的方式来处理异常。
设想一下,我们让deferred来捕获 GibberishError 与ValueError 异常,并将其传递到errback链中进行处理。如果要保留原有的行为,那么需要下面的errback来判断错误类型是否为Valuerror,如果是,那么返回原始式样的诗歌,这样一来,控制权再次回到callback链中并将原始式样的诗歌打印出来。
但有一个问题:errback并不会得到原始诗歌内容 。它只会得到由cummingsify抛出的vauleError异常。为了让errback处理这个错误,我们需要重新设计它来接收到原始式样的诗歌。
一种方法是改变cummingsify以让异常信息中包含原始式样的诗歌。这也正是我们在5.1版本中做的,其代码实现在twisted-client-5/get-poetry-1.py中。我们改写ValueError异常为CannotCummingsify异常,其能将诗歌作为其第一个参数来传递。
如果cummingsify中外部模块中一个真实存在的函数,那么其最好是通过另一个函数来捕获非GibberishError并抛出一个CannotCummingsify异常。这样,我们的poetry_main就成为:
def poetry_main():
addresses = parse_args()
from twisted.internet import reactor
poems = []
errors = [] def cummingsify_failed(err):
if err.check(CannotCummingsify):
print 'Cummingsify failed!'
return err.value.args[0]
return err def got_poem(poem):
print poem
poems.append(poem) def poem_failed(err):
print >>sys.stderr, 'The poem download failed.'
errors.append(err) def poem_done(_):
if len(poems) + len(errors) == len(addresses):
reactor.stop() for address in addresses:
host, port = address
d = get_poetry(host, port)
d.addCallback(cummingsify)
d.addErrback(cummingsify_failed)
d.addCallbacks(got_poem, poem_failed)
d.addBoth(poem_done)
而新的deferred结构如图24所示:

图24:版本5.1的deferrd调用链结构
来看看cummingsify_failed的errback回调:
def cummingsify_failed(err):
if err.check(CannotCummingsify):
print 'Cummingsify failed!'
return err.value.args[0]
return err
我们使用了Failure中的check方法来确认嵌入在Failure中的异常是否是CannotCummingsify的实例。如果是,我们返回异常的第一个参数(即原始式样诗歌)。因此,这样一来返回值就不是一个Failure了,控制权也就又回到callback链中了。否则(即异常不是CannotCummingsify的实例),我们返回一个Failure,即将错误传递到下一个errback中。
图25说明了当我们捕获一个CannotCummingsify时的调用过程:

图25:捕获一个CannotCummingsify异常
因此,当我们使用deferrd时,可以选择使用try/except来捕获异常,也可以让deferred来将异常传递到errback回调链中进行处理。
总结:
在这个部分,我们增强了客户端的Deferred的功能,实现了异常与结果在callback/errback链中“路由”。(你可以将各个回调看作成路由器,然后根据传入参数的情况来决定其返回值进入下一个stage的哪条链,或者说控制权进入下一个stage的哪个类型的回调)。虽然示例程序是虚构出来的,但它揭示了控制权在deferred的回调链中交错传递具体方向依赖于返回值的类型。
那我们是不是已经对deferred无所不知了?不,我们还会在下面的部分继续讲解deferred的更多的功能。但在第十一部分,我们先不讲这部分内容,而是实现我们的Twisted版本的诗歌下载服务器。
Python Twisted系列教程10:增强defer功能的客户端的更多相关文章
- Python Twisted系列教程17:造”回调”的另一种方法
作者:dave@http://krondo.com/just-another-way-to-spell-callback/ 译者: Cheng Luo 你可以从”第一部分 Twist理论基础“开始阅 ...
- Python Twisted系列教程5:由Twisted支持的诗歌客户端
作者:dave@http://krondo.com/twistier-poetry/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 抽象地构建客户端 在第四部分中,我们构建了第一个使用 ...
- Python Twisted系列教程4:由Twisted支持的诗歌客户端
作者:dave@http://krondo.com/twisted-poetry/ 译者:杨晓伟(采用意译) 你可以在这里从头开始阅读这个系列. 第一个twisted支持的诗歌服务器 尽管Twist ...
- Python Twisted系列教程13:使用Deferred新功能实现新客户端
作者:dave@http://krondo.com/deferred-all-the-way-down/ 译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 介绍 回忆下第10部分中的客户端5 ...
- Python Twisted系列教程21: Twisted和Haskell
作者:dave@http://krondo.com/twisted-and-haskell/ 译者: Cheng Luo 你可以从”第一部分 Twist理论基础“开始阅读:也可以从”Twisted ...
- Python Twisted系列教程19:改变之前的想法
作者:dave@http://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/ 译者: Cheng Luo 你可以从”第一部分 Twis ...
- Python Twisted系列教程14:Deferred用于同步环境
作者:dave@http://krondo.com/when-a-deferred-isnt/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列. 介绍 这部分我们要介绍Deferred的 ...
- Python Twisted系列教程8:使用Deferred的诗歌下载客户端
作者:dave@http://krondo.com/deferred-poetry/ 译者:杨晓伟(采用意译) 可以从这里从头开始阅读这个系列. 客户端4.0 我们已经对deferreds有些理解了 ...
- Python Twisted系列教程7:小插曲,Deferred
作者:dave@http://krondo.com/an-interlude-deferred/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 回调函数的后序发展 在第六部分我们认识这 ...
随机推荐
- hdu 5979 Convex(水,求面积)
Convex Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Subm ...
- 转:Hive性能优化之ORC索引–Row Group Index vs Bloom Filter Index
之前的文章<更高的压缩比,更好的性能–使用ORC文件格式优化Hive>中介绍了Hive的ORC文件格式,它不但有着很高的压缩比,节省存储和计算资源之外,还通过一个内置的轻量级索引,提升查询 ...
- 在SQL Server中快速删除重复记录
在SQL Server中快速删除重复记录 2006-07-17 21:53:15 分类: SQL Server 开发人员的噩梦——删除重复记录 想必每一位开发人员都有过类似的经历,在对数据库进行查询 ...
- MySQL show processlist 执行状态分析
1.Sleep 通常代表资源未释放,如果是通过连接池,sleep状态应该恒定在一定数量范围内 实战范例:因前端数据输出时(特别是输出到用户终端)未及时关闭数据库连接,导致因网络连接速度产 ...
- Maven项目中java类报错-Cannot resolve symbol
电脑蓝屏了,强制重启之后再打开IDEA里面的项目,所有Java类文件都在报Cannot resolve symbo错误,可以确定所有依赖的包都有引用且jar包没有冲突. 经查询找到这个解决方法: 在I ...
- request.setAttribute("username", username);//一定要保存,OGNL才能获取${username}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, ...
- 【python】类file文件处理
[flush方法] 通常由于缓冲,write不将数据写入文件,而是写入内存的缓冲区,需要使用flush写入文件,并清空缓冲区 文件的flush方法的作用是强制清空缓存写入文件.默认每行flush一下? ...
- C++纯虚函数实现
纯虚函数就是一个在基类中的虚函数,差别只是在一般的虚函数声明的后面加了"=0",虚函数允许函数通过与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,称为运行时的多态性 ...
- 用函数式编程,从0开发3D引擎和编辑器(一)
介绍 大家好,欢迎你踏上3D编程之旅- 本系列的素材来自我们的产品:Wonder-WebGL 3D引擎和编辑器 的整个开发过程,探讨了在从0开始构建3D引擎和编辑器的过程中,每一个重要的功能点.设计方 ...
- 【占位】HihoCoder 1160 : 攻城略地(并查集好题)
攻城略地 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 A.B两国间发生战争了,B国要在最短时间内对A国发动攻击.已知A国共有n个城市(城市编号1, 2, …, n),城 ...