文档章节

zg手册 之 twisted 开发(2)-- Deferreds 组件

 东昕
发布于 2014/06/24 18:01
字数 1823
阅读 160
收藏 0
点赞 0
评论 0

Deferreds 异步回调序列

Deferred 本质上是一个回调函数的集合,twisted 提供了对函数延迟调用的机制。

在 twisted 内部,使用 Deferred 对象管理回调序列。当异步请求结果返回时,使用 Deferred 对象调用回调序列中的函数。

这里通过几个例子来了解 Deferreds 使用的方式和工作的原理。


先看一个简单的例子

from twisted.internet import reactor, defer

def getDummyData(x):
    """创建一个 Deferred 对象,并返回这个对象"""
    d = defer.Deferred()
    # 2 秒钟后执行 Deferred 回调函数序列,把 x * 3 作为参数传递给回调序列中的第一个函数
    # reactor.callLater 是一个定时延迟调用的方法
    reactor.callLater(2, d.callback, x * 3)
    return d

def printData(result):
    """    打印结果    """
    print resultd = getDummyData(3)

# 添加回调函数到回调函数序列中
d.addCallback(printData)

# 4 秒钟后停止 reactor 循环(退出进程)
reactor.callLater(4, reactor.stop)

# 开始 Twisted reactor 事件循环
reactor.run()

使用 Deferred 的方式:

  1. d = defer.Deferred(), 创建 Deferred 对象。

  2. d.addCallback(printData), 添加回调函数,可以添加多个函数到回调序列中。

  3. d.callback(result),回调序列开始执行(使用result作为参数)。


在 Deferred 中,d.addCallbacks/a.addBoth 等函数都可以添加回调函数到序列中。还可以添加 errback(错误回调函数)在回调函数出错时进行处理。


Deferred 的内部机制

虽然调用 addCallback 的时候只传入参数 callback, 但是回调序列中 callback/errback 总是成对出现的。twisted 内部的源代码如下。

def addCallbacks(self, callback, errback=None,
                      callbackArgs=None, callbackKeywords=None,
                      errbackArgs=None, errbackKeywords=None):
         """
         Add a pair of callbacks (success and error) to this L{Deferred}.
         These will be executed when the 'master' callback is run.
         @return: C{self}.
         @rtype: a L{Deferred}"""
         assert callable(callback)
         assert errback == None or callable(errback)

         # 主要是这里成对添加 callback/errback
         cbs = ((callback, callbackArgs, callbackKeywords),
                (errback or (passthru), errbackArgs, errbackKeywords))
         self.callbacks.append(cbs)
 
         if self.called:
             self._runCallbacks()
         return self


当回调序列中的函数发生异常,或者返回twisted.python.failure.Failure 实例时, 下一个callback/errback回调函数对中的错误回调函数会被调用。

错误回调函数的例子:

#!/usr/bin/env python
# coding: utf-8

from twisted.internet import defer, reactor
from pprint import pprint

def cback1(result):
    raise Exception('cback1')

def cerr1(failure):
    print 'cerr1: %s' % str(failure)

def cback2(result):
    return 'succ cback2'

def cerr2(failure):
    print 'cerr2: %s' % str(failure)

d = defer.Deferred()
d.addCallbacks(callback=cback1, errback=cerr1)
d.addCallbacks(callback=cback2, errback=cerr2)
d.addCallback(callback=lambda ign:pprint('cback3'))

# 执行回调函数序列
d.callback('run')

# 退出事件循环
reactor.callLater(2, reactor.stop)
reactor.run()

运行结果

user@host:~/test $ python testErr.py
cerr2: [Failure instance: Traceback: <type 'exceptions.Exception'>: cback1
testErr.py:26:<module>
/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py:382:callback
/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py:490:_startRunCallbacks
--- <exception caught here> ---
/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py:577:_runCallbacks
testErr.py:8:cback1]'cback3'

从例子中看运行机制:

  1. 在回调序列执行过程中,当前回调函数的返回值就是下一个回调函数的第一个参数,d.callback('run') 使用字符串'run'作为第一个回调函数的参数。

  2. 当回调函数 cback1 出现异常,下一个错误处理函数 cerr2 被调用。

  3. 当 cerr2 返回后,如果返回值不是 twisted.python.failure.Failure, 调用将转回到下个 callback ( cback3 )函数,如果返回值是 Failure 对象,则继续调用下一个的错误处理函数。


Deferred 结构图


Deferred 调用图


Deferred 回调函数的参数传递和回调函数内异步操作方法

添加到 Deferred 中的回调函数可以传入参数,并且在回调函数内可以执行异步操作(异步网络请求)。

下面是一个从 京东 获取书籍信息的例子: 先从搜索的列表页获取一个书籍信息,再从详情页获取详细信息。

#!/usr/bin/env python
# coding: utf-8

import sys
import bs4 as bs

from twisted.web.client import getPage
from twisted.internet import reactor
from twisted.python.util import println
from twisted.internet import defer

class Goods(object):
    '''商品信息分析类'''
    _urlFmt='http://search.jd.com/search?keyword=%s&enc=utf-8&rt=1&book=y&area=1&wtype=1'

    _name=''
    _id=''
    _price=0
    _detail=''
    _detailUrl=''

    def __init__(self, keyword):
        self._url = self._urlFmt % keyword

    def getGoodsUrl(self):
        return self._url

    def getGoodsDetailUrl(self):
        return self._detailUrl

    def getGoods(self, page):
        soup = bs.BeautifulSoup(page)

        # 获取商品名称,详情页url
        infos = soup.find_all(class_='info')
        info = infos[0].select('dt.p-name > a')[0]
        self._name = info.get_text().strip()
        self._detailUrl = info['href']

    def getGoodsDetail(self, page):
        #print page
        soup = bs.BeautifulSoup(page)

        # 获取价格
        price = soup.select('#summary-price strong')[0]
        self._price = price.get_text().strip()

        # 获取详细信息
        details = soup.select('#product-detail-1')[0].strings
        self._detail = ''.join(details)
        return True

    def printResult(self, IsPrint=True):
        if IsPrint:
            print self._name
            print self._price
            print self._detailUrl
            print ' ' * 10, self._detail

def main():
    errback=lambda error:(println("an error occurred", error),reactor.stop())
    def goodsCallback(page, goods):
        ''''''
        #print page
        goods.getGoods(page)
        d2 = getPage(goods.getGoodsDetailUrl())
        d2.addCallbacks(callback=goods.getGoodsDetail, errback=errback)
        return d2

    # 根据关键词创建商品信息对象
    goods = Goods(sys.argv[1])

    # 异步查询商品信息
    d1 = getPage(goods.getGoodsUrl())

    # 添加获取商品信息的回调函数到 defer 对象
    d1.addCallbacks(callback=goodsCallback, errback=errback, callbackArgs=[goods])
    d1.addCallbacks(callback=lambda successed:(goods.printResult(successed)), errback=errback)

    # 结束主事件监控循环
    d1.addCallbacks(callback=lambda ign: reactor.stop(), errback=errback)

    reactor.run()

if __name__ == '__main__':
    main()

运行例子程序:

python goodsJD.py 机器学习

例子中 Deferred 对象的使用:

  1. d.addCallbacks 的 callbackArgs 参数接收一个 list 作为添加的回调函数的参数,errbackArgs 也是一个 list, 作为添加的错误处理函数的参数。

  2. 在回调序列中的执行过程中,如果回调函数返回了一个 Deferred 对象,例如上面的 goodsCallback 函数。在新返回的 d2 回调序列被执行完之前,d1的调用序列会暂停执行,在 d2 被执行完之后,d1 回调序列才会继续执行。(这个功能对于嵌套的异步请求很重要,例如我有三个请求A,B,C, 这三个请求有依赖关系,必须根据 A 的响应发送 B 请求,根据 B 的响应发送 C 请求)


DeferredList 对象的使用

有时候需要等待几个异步事件完成,然后再执行某些任务。这时候 DeferredList 就可以发挥作用了。

看官网的一个例子,我修改并注释一些代码

#!/usr/bin/env python
# coding: utf-8

from twisted.internet import defer

# DeferredList 回调函数,参数是它包含的 Deferred 执行后的结果组成的 list
def printResult(result):
    for (success, value) in result:
        if success:
            print 'Success:', value
        else:
            print 'Failure:', value.getErrorMessage()

# deferred1 的回调函数
def printDeferred(result):
    print 'deferred1: %s' % result
    return 'deferred1: %s' % result

# 创建三个新的 Deferred
deferred1 = defer.Deferred()
deferred2 = defer.Deferred()
deferred3 = defer.Deferred()

# 打包三个 Deferred 对象到 DeferredList
dl = defer.DeferredList([deferred1, deferred2, deferred3], consumeErrors=True)

# 添加回调函数到 DeferredList,
# 当它包含的 Deferred 都执行完后,将执行 DeferredList 这个调用序列
dl.addCallback(printResult)

# 注意这个回调函数添加的位置,
# 因为在 defer.DeferredList 后面,所以这个回调将不影响 DeferredList 结果
# 只有添加在 defer.DeferredList 之前,结果才会传递给 defer.DeferredList 的回调序列
deferred1.addCallback(printDeferred)

# 调用三个回调序列
deferred1.callback('one')
deferred2.errback(Exception('bang!'))
deferred3.callback('three')

运行结果

user@host ~/test/twisted $ python testDeferredList.py
deferred1: one
Success: one
Failure: bang!
Success: three

DeferredList 运行需要注意的地方

  1. DeferredList 打包的回调序列 deferred1,deferred2,deferred3,这几个序列中的回调函数要在打包之前(创建DeferredList对象之前)添加,(我例子中添加的位置是不正确的!!!)

  2. DeferredList 的回调函数接收的参数是个 list, list 中的每个元素是 (succ, result),表示 deferred1,deferred2,deferred3 执行成功的标志和最后结果


原文链接:http://www.hopez.org/blog/9/1403586037


© 著作权归作者所有

共有 人打赏支持
粉丝 10
博文 22
码字总数 16965
作品 0
浦东
架构师
Twisted DeferredList用法

DeferredList 有时候,你想等所有的事件都发生后通知你,而不是每一个都通知一下。比如,你想等待在在一个列表里的所有连接都关闭后通知你,twisted.internet.defer.DeferredList 能够完成你...

疯子张 ⋅ 2013/12/26 ⋅ 0

zg手册 之 twisted 开发(1)-- twisted 框架介绍

异步非阻塞框架 twisted 是一个事件驱动的网络开发框架,使用 python 开发。 twisted 框架编写的服务器有几个基本的元素: 应用程序对象(application):管理应用程序资源的对象,一个应用程序...

东昕 ⋅ 2014/06/21 ⋅ 0

Scrapy 1.1.0rc1 发布,支持 Python 3

Scrapy 1.1.0rc1 发布,1.1.0的更新如下: Scrapy 1.1 has beta Python 3 support (requires Twisted >= 15.5). See:ref: for more details and some limitations. Hot new features: Item ......

oschina ⋅ 2016/02/05 ⋅ 3

zg手册 之 scrapy 开发(5)-- downloader(下载器)开发

为什么需要开发下载器 定向抓取时,目标站点的数据不能单次请求获取,需要3,4次或者更多,请求之间有依赖关系,就是需要连续请求完成这个下载事物 前面讲过的 js 动态页面下载 ...... 注意事...

东昕 ⋅ 2014/04/02 ⋅ 1

Scrapy 1.1.0 发布,web 爬虫框架

Scrapy 1.1.0 发布了。Scrapy 是一套基于基于Twisted的异步处理框架,纯python实现的爬虫框架,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之...

oschina ⋅ 2016/05/12 ⋅ 5

【转】Python Twisted介绍

Python Twisted介绍 作者:Jessica McKellar 原文链接   Twisted是用Python实现的基于事件驱动的网络引擎框架。Twisted诞生于2000年初,在当时的网络游戏开发者看来,无论他们使用哪种语言...

罗兵 ⋅ 2016/06/27 ⋅ 0

twisted学习(deferred机制)

1、在异步编程中,defer机制主要是用来管理callback函数,在twisted中,许多功能的实现都是采用的事件驱动机制,在异步编程中,错误处理机制和同步程序并不一样,异步程序会无视错误执行下去...

fsxchen ⋅ 2013/09/11 ⋅ 0

zg手册 之 scrapy 开发(4)-- javascript 动态页面的抓取

javascript 动态页面 目前许多网站大量运用js脚本进行一些页面的处理,这些页面的抓取对爬虫是个挑战。 这类页面的抓取,我用到了下面的方法 分析页面(firebug/chrome调试工具 等),找到a...

东昕 ⋅ 2014/04/02 ⋅ 2

(转) Twisted : 第八部分 使用Deferred的诗歌下载客户端

客户端4.0 我们已经对deferreds有些理解了,现在我们可以使用它重写我们的客户端。你可以在twisted-client-4/get-poetry.py中看到它的实现。 这里的getpoetry已经再也不需要callback与errba...

水果糖 ⋅ 2016/01/27 ⋅ 0

AngularJS 1.4.10 发布,Web页面框架

AngularJS 1.4.10发布了。AngularJS是一组用来开发Web页面的框架、模板以及数据绑定和丰富UI组件。它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作。 AngularJS Build Status A...

oschina ⋅ 2016/03/17 ⋅ 7

没有更多内容

加载失败,请刷新页面

加载更多

下一页

istio 文档

https://istio.io/docs/concepts/ https://istio.io/docs/concepts/traffic-management/handling-failures/ https://istio.io/docs/concepts/traffic-management/rules-configuration/......

xiaomin0322 ⋅ 19分钟前 ⋅ 0

编程语言的作用及与操作系统和硬件的关系

一、编程语言的作用及与操作系统和硬件的关系 作用:编程语言是计算机语言,是一种程序员与计算机之间沟通的介质,通过编程语言可以使得计算机能够根据人的指令一步一步去工作,完成某种特定...

slagga ⋅ 30分钟前 ⋅ 0

runtime实现按钮点击事件

也不能说是实现吧,,,就是有点类似于RAC里边的写法,不用给btn添加另外的点击事件,就那个add...select...这样子很不友好,来看下代码: [self.btn handleControlEvent:UIControlEventTou...

RainOrz ⋅ 30分钟前 ⋅ 0

Windows系统运维转linux系统运维的经历

开篇之前,首先介绍一下我的背景把:我是一个三线城市的甲方运维。最近,在《Linux就该这么学》书籍的影响下和朋友小A(Linux运维已经三年了,工资也比我的高很多)的影响下,决定转行。最近...

linux-tao ⋅ 31分钟前 ⋅ 0

zip压缩工具,tar打包工具

zip压缩工具 zip打包工具跟前面说到的gzip,bz2,xz 工具最大的不一样是zip可以压缩目录。如果没有安装,需要使用yum install -y zip 来安装。安装完之后就可以直接使用了,跟之前提到的压缩...

李超小牛子 ⋅ 39分钟前 ⋅ 0

使用npm发布自己的npm组件包

一、注册npm账号 官网:https://www.npmjs.com/signup 注册之后需要进行邮箱验证,否则后面进行组件包发布时候会提示403错误,让进行邮箱核准。 二、本地新建一个文件夹,cd进入后使用npm i...

灰白发 ⋅ 41分钟前 ⋅ 0

010. 深入JVM学习—垃圾收集策略概览

1. 新生代可用GC策略 1. 串行GC(Serial Copying) 算法:复制(Copying)清理算法; 操作步骤: 扫描年轻代中所有存活的对象; 使用Minor GC进行垃圾回收,同时将存活对象保存到“S0”或“S...

影狼 ⋅ 41分钟前 ⋅ 0

JVM性能调优实践——JVM篇

在遇到实际性能问题时,除了关注系统性能指标。还要结合应用程序的系统的日志、堆栈信息、GClog、threaddump等数据进行问题分析和定位。关于性能指标分析可以参考前一篇JVM性能调优实践——性...

Java小铺 ⋅ 42分钟前 ⋅ 0

误关了gitlab sign-in 功能的恢复记录

本想关sign-up的,误点了sign-in 退出后登录界面提示: No authentication methods configured 一脸懵逼.. 百度后众多方案说修改application_settings 的 signin_enabled字段; 实际上新版本字段...

铂金蛋蛋 ⋅ 43分钟前 ⋅ 0

登录后,后续请求接口没有带登录cookie可能原因

1.XMLHttpRequest.withCredentials没设置好,参考https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials...

LM_Mike ⋅ 43分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部