最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python的Twisted框架中使用Deferred對象來管理回調(diào)函數(shù)

 更新時間:2016年05月25日 11:20:27   作者:Q_AN1314  
當(dāng)說起Twisted的異步與非阻塞模式等特性時,回調(diào)函數(shù)的使用在其中自然就顯得不可或缺,接下來我們就來看Python的Twisted框架中使用Deferred對象來管理回調(diào)函數(shù)的用法.

首先拋出我們在討論使用回調(diào)編程時的一些觀點(diǎn):

  • 激活errback是非常重要的。由于errback的功能與except塊相同,因此用戶需要確保它們的存在。他們并不是可選項(xiàng),而是必選項(xiàng)。
  • 不在錯誤的時間點(diǎn)激活回調(diào)與在正確的時間點(diǎn)激活回調(diào)同等重要。典型的用法是,callback與errback是互斥的即只能運(yùn)行其中一個。
  • 使用回調(diào)函數(shù)的代碼重構(gòu)起來有些困難。

Deferred
Twisted使用Deferred對象來管理回調(diào)函數(shù)的序列。有些情況下可能要把一系列的函數(shù)關(guān)聯(lián)到Deferred對象上,以便在在異步操作完成時按次序地調(diào)用(這些一系列的回調(diào)函數(shù)叫做回調(diào)函數(shù)鏈);同時還要有一些函數(shù)在異步操作出現(xiàn)異常時來調(diào)用。當(dāng)操作完成時,會先調(diào)用第一個回調(diào)函數(shù),或者當(dāng)錯誤發(fā)生時,會先調(diào)用第一個錯誤處理回調(diào)函數(shù),然后Deferred對象會把每個回調(diào)函數(shù)或錯誤處理回調(diào)函數(shù)的返回值傳遞給鏈中的下一個函數(shù)。
Callbacks
一個twisted.internet.defer.Deferred對象代表著在將來某個時刻一定會產(chǎn)生結(jié)果的一個函數(shù)。我們可以把一個回調(diào)函數(shù)關(guān)聯(lián)到Deferred對象上,一旦這個Deferred對象有了結(jié)果,這個回調(diào)函數(shù)就會被調(diào)用。另外,Deferred對象還允許開發(fā)者為它注冊一個錯誤處理回調(diào)函數(shù)。Deferred機(jī)制對于各種各樣的阻塞或者延時操作都為開發(fā)者提供了標(biāo)準(zhǔn)化的接口。
from twisted.internet import reactor, defer

def getDummyData(inputData):
  """
  This function is a dummy which simulates a delayed result and
  returns a Deferred which will fire with that result. Don't try too
  hard to understand this.
  """
  print('getDummyData called')
  deferred = defer.Deferred()
  # simulate a delayed result by asking the reactor to fire the
  # Deferred in 2 seconds time with the result inputData * 3
  reactor.callLater(2, deferred.callback, inputData * 3)
  return deferred

def cbPrintData(result):
  """
  Data handling function to be added as a callback: handles the
  data by printing the result
  """
  print('Result received: {}'.format(result))

deferred = getDummyData(3)
deferred.addCallback(cbPrintData)

# manually set up the end of the process by asking the reactor to
# stop itself in 4 seconds time
reactor.callLater(4, reactor.stop)
# start up the Twisted reactor (event loop handler) manually
print('Starting the reactor')
reactor.run()

多個回調(diào)函數(shù)
在一個Deferred對象上可以關(guān)聯(lián)多個回調(diào)函數(shù),這個回調(diào)函數(shù)鏈上的第一個回調(diào)函數(shù)會以Deferred對象的結(jié)果為參數(shù)來調(diào)用,而第二個回調(diào)函數(shù)以第一個函數(shù)的結(jié)果為參數(shù)來調(diào)用,依此類推。為什么需要這樣的機(jī)制呢?考慮一下這樣的情況,twisted.enterprise.adbapi返回一個Deferred對象——一個一個SQL查詢的結(jié)果,可能有某個web窗口會在這個Deferred對象上添加一個回調(diào)函數(shù),以把查詢結(jié)果轉(zhuǎn)換成HTML的格式,然后把Deferred對象繼續(xù)向前傳遞,這時Twisted會調(diào)用這個回調(diào)函數(shù)并把結(jié)果返回給HTTP客戶端。在出現(xiàn)錯誤或者異常的情況下,回調(diào)函數(shù)鏈不會被調(diào)用。

from twisted.internet import reactor, defer


class Getter:
  def gotResults(self, x):
    """
    The Deferred mechanism provides a mechanism to signal error
    conditions. In this case, odd numbers are bad.

    This function demonstrates a more complex way of starting
    the callback chain by checking for expected results and
    choosing whether to fire the callback or errback chain
    """
    if self.d is None:
      print("Nowhere to put results")
      return

    d = self.d
    self.d = None
    if x % 2 == 0:
      d.callback(x * 3)
    else:
      d.errback(ValueError("You used an odd number!"))

  def _toHTML(self, r):
    """
    This function converts r to HTML.

    It is added to the callback chain by getDummyData in
    order to demonstrate how a callback passes its own result
    to the next callback
    """
    return "Result: %s" % r

  def getDummyData(self, x):
    """
    The Deferred mechanism allows for chained callbacks.
    In this example, the output of gotResults is first
    passed through _toHTML on its way to printData.

    Again this function is a dummy, simulating a delayed result
    using callLater, rather than using a real asynchronous
    setup.
    """
    self.d = defer.Deferred()
    # simulate a delayed result by asking the reactor to schedule
    # gotResults in 2 seconds time
    reactor.callLater(2, self.gotResults, x)
    self.d.addCallback(self._toHTML)
    return self.d


def cbPrintData(result):
  print(result)


def ebPrintError(failure):
  import sys
  sys.stderr.write(str(failure))


# this series of callbacks and errbacks will print an error message
g = Getter()
d = g.getDummyData(3)
d.addCallback(cbPrintData)
d.addErrback(ebPrintError)

# this series of callbacks and errbacks will print "Result: 12"
g = Getter()
d = g.getDummyData(4)
d.addCallback(cbPrintData)
d.addErrback(ebPrintError)

reactor.callLater(4, reactor.stop)
reactor.run()

需要注意的一點(diǎn)是,在方法gotResults中處理self.d的方式。在Deferred對象被結(jié)果或者錯誤激活之前,這個屬性被設(shè)置成了None,這樣Getter實(shí)例就不會再持有將要激活的Deferred對象的引用。這樣做有幾個好處,首先,這樣可以避免Getter.gotResults有時會重復(fù)激活相同的Deferred對象的可能性(這樣會導(dǎo)致出現(xiàn)AlreadyCalledError異常)。其次,這樣做可以使得該Deferred對象上可以添加一個調(diào)用了Getter.getDummyData函數(shù)的回調(diào)函數(shù),而不會產(chǎn)生問題。還有,這樣使得Python垃圾收集器更容易通過引用循環(huán)來檢測出一個對象是否需要回收。
可視化的解釋
這里寫圖片描述

2016525111342144.jpg (503×187)
1.請求方法請求數(shù)據(jù)到Data Sink,得到返回的Deferred對象。
2.請求方法把回調(diào)函數(shù)關(guān)聯(lián)到Deferred對象上。

2016525111420537.jpg (240×382)

1.當(dāng)結(jié)果已經(jīng)準(zhǔn)備好后,把它傳遞給Deferred對象。如果操作成功就調(diào)用Deferred對象的.callback(result)方法,如果操作失敗就調(diào)用Deferred對象的.errback(faliure)方法。注意failure是twisted.python.failure.Failure類的一個實(shí)例。
2.Deferred對象使用result或者faliure來激活之前添加的回調(diào)函數(shù)或者錯誤處理回調(diào)函數(shù)。然后就按照下面的規(guī)則來沿著回調(diào)函數(shù)鏈繼續(xù)執(zhí)行下去:
回調(diào)函數(shù)的結(jié)果總是當(dāng)做第一個參數(shù)被傳遞給下一個回調(diào)函數(shù),這樣就形成了一個鏈?zhǔn)降奶幚砥鳌?br /> 如果一個回調(diào)函數(shù)拋出了異常,就轉(zhuǎn)到錯誤處理回調(diào)函數(shù)來執(zhí)行。
如果一個faliure沒有得到處理,那么它會沿著錯誤處理回調(diào)函數(shù)鏈一直傳遞下去,這有點(diǎn)像異步版本的except語句。
如果一個錯誤處理回調(diào)函數(shù)沒有拋出異?;蛘叻祷匾粋€twisted.python.failure.Failure實(shí)例,那么接下來就轉(zhuǎn)到去執(zhí)行回調(diào)函數(shù)。
錯誤處理回調(diào)函數(shù)
Deferred對象的錯誤處理模型是以Python的異常處理為基礎(chǔ)的。在沒有錯誤發(fā)生的情況下,所有的回調(diào)函數(shù)都會被執(zhí)行,一個接著一個,就像上面所說的那樣。
如果沒有執(zhí)行回調(diào)函數(shù)而是執(zhí)行了錯誤處理回調(diào)函數(shù)(比如DB查詢發(fā)生了錯誤),那么一個twisted.python.failure.Failure對象會被傳遞給第一個錯誤處理回調(diào)函數(shù)(你可以添加多個錯誤處理回調(diào)函數(shù),就像回調(diào)函數(shù)鏈一樣)??梢园彦e誤處理回調(diào)函數(shù)鏈當(dāng)做普通Python代碼中的except代碼塊。
除非在except代碼塊中顯式地raise了一個錯誤,否則Exception對象就會被捕捉到且不再繼續(xù)傳播下去,然后又開始正常地執(zhí)行程序。對于錯誤處理回調(diào)函數(shù)鏈來說也是一樣,除非你顯式地return一個Faliure或者重新拋出一個異常,否則錯誤就會停止繼續(xù)傳播,然后就會從那里開始執(zhí)行正常的回調(diào)函數(shù)鏈(使用錯誤處理回調(diào)函數(shù)返回的值)。如果錯誤處理回調(diào)函數(shù)返回了一個Faliure或者拋出了一個異常,那么這個Faliure或者異常就會被傳遞給下一個錯誤處理回調(diào)函數(shù)。
注意,如果一個錯誤處理回調(diào)函數(shù)什么也沒有返回,那它實(shí)際上返回的是None,這就意味著在這個錯誤處理回調(diào)函數(shù)執(zhí)行之后會繼續(xù)回調(diào)函數(shù)鏈的執(zhí)行。這可能不是你實(shí)際上期望的那樣,所以要確保你的錯誤處理回調(diào)函數(shù)返回一個Faliure對象(或者就是傳遞給它當(dāng)參數(shù)的那個Faliure對象)或者一個有意義的返回值以便來執(zhí)行下一個回調(diào)函數(shù)。
twisted.python.failure.Failure有一個有用的方法叫做trap,可以讓下面的代碼變成更有效率的另一種形式:

try:
  # code that may throw an exception
  cookSpamAndEggs()
except (SpamException, EggException):
  # Handle SpamExceptions and EggExceptions
  ...

可以寫成:

def errorHandler(failure):
  failure.trap(SpamException, EggException)
  # Handle SpamExceptions and EggExceptions

d.addCallback(cookSpamAndEggs)
d.addErrback(errorHandler)

如果傳遞給faliure.trap的參數(shù)沒有能和Faliure中的錯誤匹配的,那它會重新拋出這個錯誤。
還有一個需要注意的地方,twisted.internet.defer.Deferred.addCallbacks方法的功能和addCallback再跟上addErrback的功能是類似的,但不完全一樣。考慮一下下面的情況:

# Case 1
d = getDeferredFromSomewhere()
d.addCallback(callback1)    # A
d.addErrback(errback1)     # B
d.addCallback(callback2)
d.addErrback(errback2)

# Case 2
d = getDeferredFromSomewhere()
d.addCallbacks(callback1, errback1) # C
d.addCallbacks(callback2, errback2)

對于Case 1來說,如果在callback1里面發(fā)生了錯誤,那么errback1就會被調(diào)用。而對于Case 2來說,被調(diào)用的卻是是errback2。
實(shí)際上是因?yàn)椋贑ase 1中,行A會處理getDeferredFromSomewhere執(zhí)行成功的情況,行B會處理發(fā)生在getDeferredFromSomewhere執(zhí)行時或者行A的callback1執(zhí)行時的錯誤。而在Case 2中,行C中的errback1只會處理getDeferredFromSomewhere執(zhí)行時產(chǎn)生的錯誤,而不會負(fù)責(zé)callback1中產(chǎn)生的錯誤。
未處理的錯誤
如果一個Deferred對象在還有一個未處理的錯誤時(即如果它還有下一個errback就一定會調(diào)用)就被垃圾收集器清除掉了,那么Twisted會把這個錯誤的traceback記錄到日志文件里。這意味著你可能不用添加errback仍然能夠記錄錯誤。不過要小心的是,如果你還持有這個Deferred對象的引用,并且它永遠(yuǎn)不會被垃圾收集器清理,那么你就會永遠(yuǎn)看不到這個錯誤(而且你的callbacks會神秘地永遠(yuǎn)不會執(zhí)行)。如果不確定上述情況是否會發(fā)生,你應(yīng)當(dāng)在回調(diào)函數(shù)鏈之后顯式地添加一個errback,即使只是這樣寫:

# Make sure errors get logged
from twisted.python import log
d.addErrback(log.err)

處理同步和異步結(jié)果
在一些應(yīng)用中,可能同時會有同步的函數(shù),也會有異步的函數(shù)。例如,對于一個用戶認(rèn)證函數(shù),如果它是從內(nèi)存中檢查用戶是否已經(jīng)認(rèn)證,這樣就可以立即返回結(jié)果;但是如果它需要等待網(wǎng)絡(luò)上的數(shù)據(jù),那它就應(yīng)當(dāng)返回一個當(dāng)數(shù)據(jù)到達(dá)時就激活的Deferred對象。這就是說,一個想要去檢查用戶是否已經(jīng)認(rèn)證的函數(shù)需要能同時接受立即返回的結(jié)果和Deferred對象。
下面的例子中,authenticateUser使用了isValidUser來認(rèn)證用戶:

def authenticateUser(isValidUser, user):
  if isValidUser(user):
    print("User is authenticated")
  else:
    print("User is not authenticated")

這個函數(shù)假定isValidUser是立即返回的,然而實(shí)際上isValidUser可能是異步認(rèn)證用戶的并且返回的是一個Deferred對象。把這個函數(shù)調(diào)整為既能接收同步的isValidUser又能接收異步的isValidUser是有可能的。同時把同步的函數(shù)改成返回值為Deferred對象也是可以的。
在庫函數(shù)代碼中處理可能的Deferred對象
這是一個可能被傳遞給authenticateUser的同步的用戶認(rèn)證方法:

def synchronousIsValidUser(user):
  '''
  Return true if user is a valid user, false otherwise
  '''
  return user in ["Alice", "Angus", "Agnes"]

這是一個異步的用戶認(rèn)證方法,返回一個Deferred對象:

from twisted.internet import reactor, defer

def asynchronousIsValidUser(user):
  d = defer.Deferred()
  reactor.callLater(2, d.callback, user in ["Alice", "Angus", "Agnes"])
  return d

我們最初對authenticateUser的實(shí)現(xiàn)希望isValidUser是同步的,但是現(xiàn)在需要把它改成既能處理同步又能處理異步的isValidUser實(shí)現(xiàn)。對此,可以使用maybeDeferred函數(shù)來調(diào)用isValidUser,這個函數(shù)可以保證isValidUser函數(shù)的返回值是一個Deferred對象,即使isValidUser是一個同步的函數(shù):

from twisted.internet import defer

def printResult(result):
  if result:
    print("User is authenticated")
  else:
    print("User is not authenticated")

def authenticateUser(isValidUser, user):
  d = defer.maybeDeferred(isValidUser, user)
  d.addCallback(printResult)

現(xiàn)在isValidUser無論是同步的還是異步的都可以了。
也可以把synchronousIsValidUser函數(shù)改寫成返回一個Deferred對象,可以參考這里。
取消回調(diào)函數(shù)
動機(jī):一個Deferred對象可能需要很長時間才會調(diào)用回調(diào)函數(shù),甚至于永遠(yuǎn)也不會調(diào)用。有時候可能沒有那么好的耐心來等待Deferred返回結(jié)果。既然Deferred完成后要執(zhí)行的所有代碼都在你的應(yīng)用中或者調(diào)用的庫中,那么你就可以選擇在已經(jīng)過去了很長時間才收到結(jié)果時忽略這個結(jié)果。然而,即使你選擇忽略這個結(jié)果,這個Deferred對象產(chǎn)生的底層操作仍然在后臺工作著,并且消耗著機(jī)器資源,比如CPU時間、內(nèi)存、網(wǎng)絡(luò)帶寬甚至磁盤容量。因此,當(dāng)用戶關(guān)閉窗口,點(diǎn)擊了取消按鈕,從你的服務(wù)器上斷開連接或者發(fā)送了一個“停止”的指令,這時你需要顯式地聲明你對之前原定的操作的結(jié)果已經(jīng)不再感興趣了,以便原先的Deferred對象可以做一些清理的工作并釋放資源。
這是一個簡單的例子,你想連接到一個外部的機(jī)器,但是這個機(jī)器太慢了,所以需要在應(yīng)用中添加一個取消按鈕來終止這次連接企圖,以便用戶可以連接到另一個機(jī)器。這里是這樣的一個應(yīng)用的大概邏輯:

def startConnecting(someEndpoint):
  def connected(it):
    "Do something useful when connected."
  return someEndpoint.connect(myFactory).addCallback(connected)
# ...
connectionAttempt = startConnecting(endpoint)
def cancelClicked():
  connectionAttempt.cancel()

顯然,startConnecting被一些UI元素用來讓用戶選擇連接哪個機(jī)器。然后是一個取消按鈕陪著到cancelClicked函數(shù)上。
當(dāng)connectionAttempt.cancel被調(diào)用時,會發(fā)生以下操作:

  • 導(dǎo)致潛在的連接操作終止,如果它仍然在進(jìn)行中的話
  • 不管怎樣,使得connectionAttempt這個Deferred對象及時地被完成
  • 有可能導(dǎo)致connectionAttempt這個Deferred對象因?yàn)镃ancelledError錯誤調(diào)用錯誤處理函數(shù)

即使這個取消操作已經(jīng)表達(dá)了讓底層的操作停止的需求,但是底層的操作不大可能馬上就對此作出反應(yīng)。甚至在這個簡單的例子中就有一個不會被中斷的操作:域名解析,因此需要在在一個線程中執(zhí)行;這個應(yīng)用中的連接操作如果在等待域名解析的時候就不能被取消。所以你要取消的Deferred對象可能不會立即調(diào)用回調(diào)函數(shù)或錯誤處理回調(diào)函數(shù)。

一個Deferred對象可能會在執(zhí)行它的回調(diào)函數(shù)鏈的任何一點(diǎn)時等待另一個Deferred對象的完成。沒有方法可以在回調(diào)函數(shù)鏈的特定一個點(diǎn)知道是否每件事都已經(jīng)準(zhǔn)備好了。由于有可能一個回調(diào)函數(shù)鏈的很多層次上的函數(shù)都會希望取消同一個Deferred對象,那么鏈上任何層次的函數(shù)在任意時刻都有可能調(diào)用.cancel()函數(shù)。.cancel()函數(shù)從不拋出任何異?;蛘叻祷厝魏沃?。你可以重復(fù)調(diào)用它,即使這個Deferred對象已經(jīng)被激活了,它已經(jīng)沒有剩余的回調(diào)函數(shù)了。
在實(shí)例化了一個Deferred對象的同時,可以給它提供一個取消函數(shù)(Deferred對象的構(gòu)造函數(shù)為def __init__(self, canceller=None): (source)),這個canceller可以做任何事情。理想情況下,它做的每件事情都都會阻止之前你請求的操作,但是并不總是能保證這樣。所以Deferred對象的取消只是盡力而為。原因有幾點(diǎn):
Deferred對象不知道怎樣取消底層的操作。
底層的操作已經(jīng)執(zhí)行到了一個不可取消的狀態(tài),因?yàn)榭赡芤呀?jīng)執(zhí)行了一些不可逆的操作。
Deferred對象可能已經(jīng)有了結(jié)果,所以沒有要取消的東西了。
調(diào)用cancel()函數(shù)后,不管是否能取消,總會得到成功的結(jié)果,不會出現(xiàn)出錯的情況。在第一種和第二和情況下,由于底層的操作仍在繼續(xù),Deferred對象大可以twisted.internet.defer.CancelledError為參數(shù)來調(diào)用它的errback。
如果取消的Deferred對象正在等待另一個Deferred對象,那么取消操作會向前傳遞到此Deferred對象。
可以參考API。
默認(rèn)的取消行為
所有的Deferred對象都支持取消,但是只提供了很簡單的行為,也沒有釋放任何資源。
考慮一下下面的例子:

operation = Deferred()
def x(result):
  print("Hooray, a result:" + repr(x))
operation.addCallback(x)
# ...
def operationDone():
  operation.callback("completed")

如果需要取消operation這個Deferred對象,而operation沒有一個canceller的取消函數(shù),就會產(chǎn)生下面兩種之一的結(jié)果:
如果operationDone已經(jīng)被調(diào)用了,也就是operation對象已經(jīng)完成了,那么什么都不會改變。operation仍然有一個結(jié)果,不過既然沒有其他的回調(diào)函數(shù)了,所以沒有什么行為上可以看到的變化。
如果operationDone已經(jīng)還沒有被調(diào)用,那么operation會馬上以CancelledError為參數(shù)激活errback。
在正常情況下,如果一個Deferred對象已經(jīng)調(diào)用了回調(diào)函數(shù)再來調(diào)用callback會導(dǎo)致一個AlreadyCalledError。因此,callback可以在已經(jīng)取消的、但是沒有canceller的Deferred對象上再調(diào)用一次,只會導(dǎo)致一個空操作。如果你多次調(diào)用callback,仍會得到一個AlreadyCalledError異常。
創(chuàng)建能取消的Deferred對象:自定義取消函數(shù)
假設(shè)你在實(shí)現(xiàn)一個HTTP客戶端,返回一個在服務(wù)器返回響應(yīng)的時候會激活的Deferred對象。取消最好是關(guān)閉連接。為了讓取消函數(shù)這么做,可以向Deferred對象的構(gòu)造函數(shù)中傳遞一個函數(shù)作為參數(shù)(當(dāng)Deferred對象被取消的時候會調(diào)用這個函數(shù)):

class HTTPClient(Protocol):
  def request(self, method, path):
    self.resultDeferred = Deferred(
      lambda ignore: self.transport.abortConnection())
    request = b"%s %s HTTP/1.0\r\n\r\n" % (method, path)
    self.transport.write(request)
    return self.resultDeferred

  def dataReceived(self, data):
    # ... parse HTTP response ...
    # ... eventually call self.resultDeferred.callback() ...

現(xiàn)在如果在HTTPClient.request()返回的Deferred對象上調(diào)用了cancel()函數(shù),這個HTTP請求就會取消(如果沒有太晚的話)。要注意的是,還要在一個已經(jīng)被取消的、帶有canceller的Deferred對象上調(diào)用callback()。
DeferredList
有時你想在幾個不同的事件都發(fā)生后再得到通知,而不是每個事件發(fā)生都會通知一下。例如,你想等待一個列表中所有的連接都關(guān)閉。twisted.internet.defer.DeferredList就適用于這種情況。
用多個Deferred對象來創(chuàng)建一個DeferredList,只需傳遞一個你想等待的Deferred對象的列表即可:
# Creates a DeferredList
dl = defer.DeferredList([deferred1, deferred2, deferred3])

現(xiàn)在就可以把這個DeferredList當(dāng)成一個普通的Deferred來看待了,例如你也可以調(diào)用addCallbacks等等。這個DeferredList會在所有的Deferred對象都完成之后才調(diào)用它的回調(diào)函數(shù)。這個回調(diào)函數(shù)的參數(shù)是這個DeferredList對象中包含的所有Deferred對象返回結(jié)果的列表,例如:

# A callback that unpacks and prints the results of a DeferredList
def printResult(result):
  for (success, value) in result:
    if success:
      print('Success:', value)
    else:
      print('Failure:', value.getErrorMessage())

# Create three deferreds.
deferred1 = defer.Deferred()
deferred2 = defer.Deferred()
deferred3 = defer.Deferred()

# Pack them into a DeferredList
dl = defer.DeferredList([deferred1, deferred2, deferred3], consumeErrors=True)

# Add our callback
dl.addCallback(printResult)

# Fire our three deferreds with various values.
deferred1.callback('one')
deferred2.errback(Exception('bang!'))
deferred3.callback('three')

# At this point, dl will fire its callback, printing:
#  Success: one
#  Failure: bang!
#  Success: three
# (note that defer.SUCCESS == True, and defer.FAILURE == False)

正常情況下DeferredList不會調(diào)用errback,但是除非把cousumeErrors設(shè)置成True,否則在Deferred對象中產(chǎn)生的錯誤仍然會激活每個Deferred對象各自的errback。
注意,如果你想在添加到DeferredList中去的Deferred對象上應(yīng)用回調(diào)函數(shù),那么就需要注意添加回調(diào)函數(shù)的時機(jī)。把一個Deferred對象添加到DeferredList中會導(dǎo)致同時也給該Deferred對象添加了一個回調(diào)函數(shù)(當(dāng)這個回調(diào)函數(shù)運(yùn)行的時候,它的功能是檢查DeferredList是否已經(jīng)完成了)。最重要的是,變量這個回調(diào)函數(shù)把記錄了Deferred對象的返回值并把這個值傳遞到最終交給DeferredList回調(diào)函數(shù)當(dāng)做參數(shù)的列表中。
因此,如果你在把一個Deferred添加到DeferredList之后又給這個Deferred對象添加了一個回調(diào)函數(shù),那么這個新添加的回調(diào)函數(shù)的返回值不會被傳遞到DeferredList的回調(diào)函數(shù)中。為了避免這種情況的發(fā)生,建議不要在把一個Deferred對象添加到DeferredList中之后再給這個Deferred添加回調(diào)函數(shù)。

def printResult(result):
  print(result)

def addTen(result):
  return result + " ten"

# Deferred gets callback before DeferredList is created
deferred1 = defer.Deferred()
deferred2 = defer.Deferred()
deferred1.addCallback(addTen)
dl = defer.DeferredList([deferred1, deferred2])
dl.addCallback(printResult)
deferred1.callback("one") # fires addTen, checks DeferredList, stores "one ten"
deferred2.callback("two")
# At this point, dl will fire its callback, printing:
#   [(True, 'one ten'), (True, 'two')]

# Deferred gets callback after DeferredList is created
deferred1 = defer.Deferred()
deferred2 = defer.Deferred()
dl = defer.DeferredList([deferred1, deferred2])
deferred1.addCallback(addTen) # will run *after* DeferredList gets its value
dl.addCallback(printResult)
deferred1.callback("one") # checks DeferredList, stores "one", fires addTen
deferred2.callback("two")
# At this point, dl will fire its callback, printing:
#   [(True, 'one), (True, 'two')]

DeferredList接受三個關(guān)鍵字參數(shù)來定制它的行為:fireOnOneCallback、fireOnOneErrback和cousumeErrors。如果設(shè)置了fireOnOneCallback,那么只要有一個Deferred對象調(diào)用了它的回調(diào)函數(shù),DeferredList就會立即調(diào)用它的回調(diào)函數(shù)。相似的,如果設(shè)置了fireOnOneErrback,那么只要有一個Deferred調(diào)用了errback,DeferredList就會調(diào)用它的errback。注意,DeferredList只是一次性的,所以在一次callback或者errback調(diào)用之后,它就會什么也不做(它會忽略它的Deferred傳遞給它的所有結(jié)果)。
fireOnOneErrback選項(xiàng)在你想等待所有事情成功執(zhí)行,而且需要在出錯時馬上知道的情形下是很有用的。
consumeErrors參數(shù)會使DeferredList中包含的Deferred對象中產(chǎn)生的錯誤在建立了DeferredList對象之后,不會傳遞給原來每個Deferred對象各自的errbacks。創(chuàng)建了DeferredList對象之后,任何單個Deferred對象中產(chǎn)生的錯誤會被轉(zhuǎn)化成結(jié)果為None的回調(diào)調(diào)用。用這個選項(xiàng)可以防止它所包含的Deferred中的“Unhandled error in Deferred”警告,而不用添加額外的errbacks(否則要消除這個警告就需要為每個Deferred對象添加errback)。 給consumeErrors參數(shù)傳遞一個True不會影響fireOnOneCallback和fireOnOneErrback的行為。應(yīng)該總是使用這個參數(shù),除非你想在將來給這些列表中的Deferred對象添加callbacks或errbacks,或者除非你知道它們不會產(chǎn)生錯誤。否則,產(chǎn)生錯誤的話會導(dǎo)致一個被Twisted記錄到日志中的“unhandled error”。
DeferredList一個普遍的用法是把一些并行的異步操作結(jié)果組合到一起。如果所有的操作都成功了,那就可以操作成功,如果有一個操作失敗了,那么就操作失敗。twisted.internet.defer.gatherResults是一個快捷方式:

from twisted.internet import defer
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.gatherResults([d1, d2], consumeErrors=True)

def cbPrintResult(result):
  print(result)

d.addCallback(cbPrintResult)

d1.callback("one")
# nothing is printed yet; d is still awaiting completion of d2
d2.callback("two")
# printResult prints ["one", "two"]

鏈?zhǔn)降腄eferred
如果你需要一個Deferred對象來等待另一個Deferred對象的執(zhí)行,你所要做的只是從它的回調(diào)函數(shù)鏈中的回調(diào)函數(shù)中返回一個Deferred對象。具體點(diǎn),如果你從某個Deferred對象A的一個回調(diào)函數(shù)中返回Deferred對象B,那么A的回調(diào)函數(shù)鏈就會在B的callback()函數(shù)調(diào)用之前進(jìn)行等待。此時,A的下一個回調(diào)函數(shù)的第一個參數(shù)就是B的最后一個回調(diào)函數(shù)返回的結(jié)果。
注意,如果一個`Deferred`對象在它的回調(diào)函數(shù)中直接或者間接地返回了它本身,那么這樣的行為是沒有定義的。代碼會試圖檢測出這種情況然后給出警告。在將來可能會直接拋出異常。
如果這看起來有點(diǎn)復(fù)雜,也不要擔(dān)心——當(dāng)你遇到這種情況的時候,你可能會直接認(rèn)出來并且知道為什么會產(chǎn)生這樣的結(jié)果。如果你需要手動地把Deferred對象
鏈接起來,這里有一個方便的方法:

chainDeferred(otherDeferred)

總結(jié)
我們認(rèn)識到了deferred是如何幫我們解決這些問題的:
我們不能忽視errback,在任何異步編程的API中都需要它。Deferred支持errbacks。
激活回調(diào)多次可能會導(dǎo)致很嚴(yán)重的問題。Deferred只能被激活一次,這就類似于同步編程中的try/except的處理方法。
含有回調(diào)的程序在重構(gòu)時相當(dāng)困難。有了deferred,我們就通過修改回調(diào)鏈來重構(gòu)程序。

相關(guān)文章

  • 圖文詳解Python中模塊或py文件導(dǎo)入(超詳細(xì)!)

    圖文詳解Python中模塊或py文件導(dǎo)入(超詳細(xì)!)

    導(dǎo)入文件目的就是為了執(zhí)行文件,下面這篇文章主要給大家介紹了關(guān)于Python中模塊或py文件導(dǎo)入的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • python如果快速判斷數(shù)字奇數(shù)偶數(shù)

    python如果快速判斷數(shù)字奇數(shù)偶數(shù)

    這篇文章主要介紹了python如果快速判斷數(shù)字奇數(shù)偶數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • 如何使用Python對日期和時間進(jìn)行排序

    如何使用Python對日期和時間進(jìn)行排序

    本文將教我們?nèi)绾问褂肞ython對日期和時間進(jìn)行排序,我們還將學(xué)習(xí)datetime模塊和sorted方法,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2023-06-06
  • Tensorflow加載與預(yù)處理數(shù)據(jù)詳解實(shí)現(xiàn)方法

    Tensorflow加載與預(yù)處理數(shù)據(jù)詳解實(shí)現(xiàn)方法

    讀取大型數(shù)據(jù)集并對其進(jìn)行有效預(yù)處理可能對其他深度學(xué)習(xí)庫來說很難實(shí)現(xiàn),但是TensorFlow借助Data API很容易實(shí)現(xiàn):只需創(chuàng)建一個數(shù)據(jù)集對象,并告訴它如何從何處獲取數(shù)據(jù)以及如何對其進(jìn)行轉(zhuǎn)換
    2022-11-11
  • Python腳本獲取操作系統(tǒng)版本信息

    Python腳本獲取操作系統(tǒng)版本信息

    本文給大家分享的小技巧是如何利用Python腳本獲取Windows和Linux的系統(tǒng)版本信息,非常的簡單實(shí)用,有需要的小伙伴可以查看下
    2016-12-12
  • Python中的Classes和Metaclasses詳解

    Python中的Classes和Metaclasses詳解

    這篇文章主要介紹了Python中的Classes和Metaclasses詳解,屬于基礎(chǔ)知識中類與對象的概念部分的深入,需要的朋友可以參考下
    2015-04-04
  • Python提取頻域特征知識點(diǎn)淺析

    Python提取頻域特征知識點(diǎn)淺析

    在本文里我們給大家分享了關(guān)于Python提取頻域特征的相關(guān)知識點(diǎn)以及基礎(chǔ)內(nèi)容,需要的朋友們跟著學(xué)習(xí)下。
    2019-03-03
  • python使用tcp實(shí)現(xiàn)局域網(wǎng)內(nèi)文件傳輸

    python使用tcp實(shí)現(xiàn)局域網(wǎng)內(nèi)文件傳輸

    這篇文章主要介紹了python使用tcp實(shí)現(xiàn)局域網(wǎng)內(nèi)文件傳輸,文件包括文本,圖片,視頻等,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • 一些Python中的二維數(shù)組的操作方法

    一些Python中的二維數(shù)組的操作方法

    這篇文章主要介紹了一些Python中的二維數(shù)組的操作方法,是Python學(xué)習(xí)當(dāng)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-05-05
  • python tkinter實(shí)現(xiàn)屏保程序

    python tkinter實(shí)現(xiàn)屏保程序

    這篇文章主要為大家詳細(xì)介紹了python tkinter實(shí)現(xiàn)屏保程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07

最新評論

大关县| 宜宾市| 承德市| 小金县| 渭源县| 额济纳旗| 马山县| 确山县| 卢湾区| 池州市| 通州市| 汕头市| 酉阳| 彭阳县| 正定县| 宾川县| 龙州县| 民和| 五峰| 清丰县| 嵩明县| 儋州市| 甘洛县| 五河县| 凉城县| 黄平县| 漳州市| 手机| 佳木斯市| 青龙| 文安县| 武安市| 德钦县| 榆树市| 永昌县| 开江县| 西盟| 大洼县| 元阳县| 新余市| 吴旗县|