日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

twisted系列教程六–继续重构twisted poetry client

發(fā)布時(shí)間:2025/3/15 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 twisted系列教程六–继续重构twisted poetry client 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Poetry for Everyone

我們已經(jīng)在我們的client取得了很大的進(jìn)步,我們的2.0版本已經(jīng)試用了Transports,Protocols 和Protocols Factories.但是仍有很多可以提升的地方.2.0 版本的client版本僅僅可以在命令行下載詩(shī).這是因?yàn)镻oetryClientFactory 不僅僅負(fù)責(zé)下載詩(shī),也負(fù)責(zé)在下載完的時(shí)候停掉這個(gè)程序.這對(duì)一個(gè)Protocol Factory類來(lái)說(shuō)太奇怪了,它應(yīng)該只用來(lái)創(chuàng)建PoetryProtocols 和 收集已經(jīng)運(yùn)行下載完的詩(shī).

我們需要一種可以把這首詩(shī)交給我們代碼的方法,但是你必須先獲取這首詩(shī),在一個(gè)同步的程序中我們可以這樣做:

def get_poetry(host, post):
????"""Return a poem from the poetry server at the given host and port."""

但是當(dāng)然的,我們不能在這里這樣做.上面的代碼會(huì)阻塞直到這首詩(shī)被完全的接收,否則的話它不會(huì)像它注釋中說(shuō)明的一樣.但是這是一個(gè)reactive 程序,網(wǎng)絡(luò)阻塞可以被它很好的解決.我們需要一個(gè)可以在詩(shī)下載完的時(shí)候通知我們的代碼,以及在下載時(shí)不阻塞的方法.這個(gè)問(wèn)題就像twisted 遇到的問(wèn)題一樣,twisted 需要在一個(gè)socket 可以進(jìn)行I/O 的時(shí)候告訴我們.twisted 用callback 的方法很好的解決了這個(gè)問(wèn)題,所以我們也可以這樣用:

def get_poetry(host, port, callback):
????"""
????Download a poem from the given host and port and invoke

??????callback(poem)

????when the poem is complete.
????"""

現(xiàn)在我們有了一個(gè)可以讓twisted 使用的api 了,讓我們繼續(xù).

就像我以前說(shuō)的,我們有時(shí)會(huì)不按照twisted 的方式的寫(xiě)代碼,上面的寫(xiě)法就不是twisted 的寫(xiě)法,我們會(huì)在第七部分和八部分用twisted 的方式來(lái)改寫(xiě)它.用最簡(jiǎn)單的方式開(kāi)始寫(xiě)代碼可以讓我們更深入的理解.

Client 3.0
你可以看到我們的poety client 3.0 版本在twisted-client-3/get-poetry.py,這個(gè)版本有一個(gè)get_poetry 方法的實(shí)現(xiàn):

def get_poetry(host, port, callback):
????from twisted.internet import reactor
????factory = PoetryClientFactory(callback)
????reactor.connectTCP(host, port, factory)

需要注意的是我們傳遞callback 給PoetryClientFactory,factory 用這個(gè)callback傳遞詩(shī):

class PoetryClientFactory(ClientFactory):

????protocol = PoetryProtocol

????def __init__(self, callback):
????????self.callback = callback

????def poem_finished(self, poem):
????????self.callback(poem)

現(xiàn)在的factory 比client 2.1 版本的簡(jiǎn)單多了,因?yàn)閒actory 不用再去關(guān)心停掉reactor 了,也少了捕捉錯(cuò)誤的代碼,我們一會(huì)會(huì)加上的.而 PoetryProtocol 則不需要做任何改變,我們可以重用它.:

class PoetryProtocol(Protocol):

????poem = ''

????def dataReceived(self, data):
????????self.poem += data

????def connectionLost(self, reason):
????????self.poemReceived(self.poem)

????def poemReceived(self, poem):
????????self.factory.poem_finished(poem)

在這些改變之后,get_poetry,PoetryClientFactory,PoetryProtocol都可以完全重用了.它們都只負(fù)責(zé)下載詩(shī).所有的初始化,關(guān)閉reactor 的邏輯代碼全部在我們的主函數(shù)poetry_main:

def poetry_main():
????addresses = parse_args()

????from twisted.internet import reactor

????poems = []

????def got_poem(poem):
????????poems.append(poem)
????????if len(poems) == len(addresses):
????????????reactor.stop()

????for address in addresses:
????????host, port = address
????????get_poetry(host, port, got_poem)

????reactor.run()

????for poem in poems:
????????print poem

我們可以把這些可以重用的部分都放到一個(gè)模塊中,然后任何人都可以獲取詩(shī)了.^_^.

順便說(shuō)一下,在你實(shí)際的測(cè)試client 3.0 的時(shí)候,你可以重新配置一下poetry server 讓它一次多輸出一些數(shù)據(jù)塊.

Discussion
我們可以把詩(shī)傳遞的過(guò)程用圖片十一 形象化:



圖片十一

圖片十一是值得多想想的,到現(xiàn)在為止我們描述的callback 鏈?zhǔn)且晕覀冏约簩?xiě)的代碼終止的.但是當(dāng)你用twisted或者其他的reactive 系統(tǒng) 寫(xiě)程序的時(shí)候,我們的callback 鏈會(huì)出現(xiàn)一小段代碼callback另一小段代碼的情況.也就是說(shuō),reactive 類型的程序在到達(dá)我們寫(xiě)的代碼的時(shí)候不會(huì)停止,它會(huì)不斷callback 下去.

在你選擇twisted 的時(shí)候,請(qǐng)把下面的話記到你的心里.當(dāng)你做了這個(gè)決定之后:

我要用twisted了啦啦啦

你也要做這個(gè)決定:

我將要把我的程序構(gòu)造成由reactor觸發(fā)的一系列的callback

也許你現(xiàn)在不會(huì)大聲地將它說(shuō)出來(lái),但twisted 就是這樣的.twisted 就是這樣工作的.可能大多數(shù)的python程序是同步的而且大多數(shù)的python 模塊也是同步的.假如我們正在寫(xiě)同步(原文這里寫(xiě)的是同步,我懷疑有點(diǎn)問(wèn)題,應(yīng)該是異步)的程序然后忽然意識(shí)到我們需要獲取詩(shī),我們可以使用get_poetry 函數(shù),就像下面的寫(xiě)法:

...
import poetrylib # I just made this module name up
poem = poetrylib.get_poetry(host, port)
...

然后我們繼續(xù),假如不久以后我們根本不需要詩(shī),然后就可以刪除上面的兩行,對(duì)所有的程序都不會(huì)造成什么影響.但是假如我們正在寫(xiě)一個(gè)同步的程序然后決定用twisted 版本的額get_poetry,我們就需要用callbacks 來(lái)重構(gòu)我們的程序.我們可能會(huì)對(duì)代碼改動(dòng)很多.我并不是說(shuō)重寫(xiě)代碼是一個(gè)錯(cuò)誤,根據(jù)我們的需求去重構(gòu)代碼是很有意義的.但它不會(huì)只增加幾行代碼那么簡(jiǎn)單.簡(jiǎn)單來(lái)講,同步的和異步的程序不能混合在一起.

如果你對(duì)twisted 和異步編程了解不是很多,我還是建議你開(kāi)始研究大型的twisted 程序代碼庫(kù)之前自己先實(shí)現(xiàn)幾個(gè)簡(jiǎn)單的demo. 這樣的話你會(huì)在沒(méi)有其他復(fù)雜的干擾下找到twisted 的感覺(jué).假如你的程序已經(jīng)是異步的,和twisted結(jié)合起來(lái)就會(huì)相對(duì)簡(jiǎn)單.twisted 和 pyGTK 和 pyQT 就結(jié)合的很好.

When Things Go Wrong
在client 3.0 版本中我們不再監(jiān)測(cè)當(dāng)連接服務(wù)器時(shí)出現(xiàn)的錯(cuò)誤,就像在client 1.0 版本中的那樣.假如我們讓client 3.0 從一個(gè)不存在的server上下載詩(shī)的話,client 3.0 不會(huì)崩潰掉而是在原地不停的等待.clientConnectionFailed callback 仍舊會(huì)被調(diào)用,但是ClientFactory中的默認(rèn)clientConnectionFailed 什么也不做,所以got_poem callback 永遠(yuǎn)不會(huì)被調(diào)用,reactor 永遠(yuǎn)不會(huì)停,然后我們又成功的寫(xiě)了另一個(gè)什么也不做的程序.

很明顯的我們需要來(lái)處理這個(gè)錯(cuò)誤,但是在哪里呢?錯(cuò)誤信息通過(guò)clientConnectionFailed 被傳遞到factory,所以我們從這里開(kāi)始,但是這個(gè)factory 應(yīng)該是可用的,正常的處理錯(cuò)誤的方法應(yīng)該依據(jù)factory被調(diào)用的地方的上下文來(lái)處理.在一些程序中,接收不到詩(shī)歌可能會(huì)是一個(gè)災(zāi)難,在令一些程序中,我們?nèi)耘f可以繼續(xù)運(yùn)行(這里是在說(shuō)twisted 的容錯(cuò)性比較強(qiáng)).

換句話說(shuō),當(dāng)你用get_poetry 的時(shí)候需要知道什么時(shí)候會(huì)出錯(cuò),不僅僅是什么時(shí)候是對(duì)的.在一個(gè)同步的系統(tǒng)中,get_poetry 會(huì)拋出一個(gè)異常,然后用一個(gè)try/except 進(jìn)行捕捉,但是在一個(gè)reactive 的系統(tǒng)里,錯(cuò)誤信息也必須以異步的方式傳遞.畢竟我們直到get_poetry 返回的時(shí)候我們才能發(fā)現(xiàn)連接錯(cuò)誤了,下面是一種可能的情況:

def get_poetry(host, port, callback):
????"""
????Download a poem from the given host and port and invoke

??????callback(poem)

????when the poem is complete. If there is a failure, invoke:

??????callback(None)

????instead.
????"""

通過(guò)監(jiān)測(cè)callback 的參數(shù),client可以確定是否我們最終得到了一首詩(shī).這樣就可以防止我們的程序永遠(yuǎn)運(yùn)行下去,但是還是會(huì)有一些小問(wèn)題,當(dāng)你向callback傳遞None的時(shí)候,并不能概括到所有的出錯(cuò)信息,而且twisted 的一些api 也會(huì)默認(rèn)的返回None,所以這里我們要用err 參數(shù)來(lái)替代None,err中可以包含具體的出錯(cuò)信息.就像下面的一樣:

def get_poetry(host, port, callback):
????"""
????Download a poem from the given host and port and invoke

??????callback(poem)

????when the poem is complete. If there is a failure, invoke:

??????callback(err)

????instead, where err is an Exception instance.
????"""

如果這里用一個(gè)異常就基本上和我們的同步程序一樣了.現(xiàn)在我們可以從異常中獲取出錯(cuò)的信息.正常的,在我們?cè)谄匠5膒ython代碼中如果遇到了異常我們會(huì)輸出traceback供我們調(diào)試用.
請(qǐng)記住在我們的callback被觸發(fā)的時(shí)候我們并不想要一個(gè)traceback.我們真正想要的是在出現(xiàn)異常的地方的exception實(shí)例 和 當(dāng)時(shí)的traceback.
twisted 包含了一個(gè)叫做Failure 的抽象,failure是Exception 和 traceback 的封裝.Failure 文檔描述了怎樣創(chuàng)建一個(gè)failure.通過(guò)傳遞給callback一個(gè)Failure對(duì)象,我們可以很好的保護(hù)traceback 信息.

在 twisted-failure/failure-examples.py中有一些Failure 對(duì)象的用法,它演示了Failure 是怎樣保護(hù)traceback信息的,即使在一個(gè)except 代碼塊上下文之外.我們現(xiàn)在不會(huì)在怎樣建立Failure 實(shí)例上花費(fèi)很多時(shí)間,在第七部分,我們會(huì)看到Failure 的用法.

第三個(gè)版本的get_poetry:

def get_poetry(host, port, callback):
????"""
????Download a poem from the given host and port and invoke

??????callback(poem)

????when the poem is complete. If there is a failure, invoke:

??????callback(err)

????instead, where err is a twisted.python.failure.Failure instance.
????"""

在這個(gè)版本后,我們?cè)诘玫疆惓5臅r(shí)候還能同時(shí)得到一個(gè)traceback 記錄.

我們已經(jīng)快完成了,但是還有一個(gè)問(wèn)題.處理錯(cuò)誤和處理正常的結(jié)果看起來(lái)是一種很奇怪的行為.一般來(lái)說(shuō),我們會(huì)對(duì)出錯(cuò)和正常結(jié)果做出完全不同的操作.在同步的系統(tǒng)中我們會(huì)用try/except 語(yǔ)句來(lái)分別處理正確的和錯(cuò)誤的結(jié)果:

try:
????attempt_to_do_something_with_poetry()
except RhymeSchemeViolation:
????# the code path when things go wrong
else:
????# the code path when things go so, so right baby

如果我們也想保持這種錯(cuò)誤處理的方式,我們需要讓錯(cuò)誤處理走另一條路徑.在異步程序中分出一個(gè)路徑意味著多出一個(gè)callback.

def get_poetry(host, port, callback, errback):
????"""
????Download a poem from the given host and port and invoke

??????callback(poem)

????when the poem is complete. If there is a failure, invoke:

??????errback(err)

????instead, where err is a twisted.python.failure.Failure instance.
????"""

Client 3.1
client 3.1 在twisted-client-3/get-poetry-1.py 中,變化還是非常直觀的, PoetryClientFactory 會(huì)獲得一個(gè)callback 和一個(gè)errback,在clientConnectionFailed中調(diào)用了errback.

class PoetryClientFactory(ClientFactory):

????protocol = PoetryProtocol

????def __init__(self, callback, errback):
????????self.callback = callback
????????self.errback = errback

????def poem_finished(self, poem):
????????self.callback(poem)

????def clientConnectionFailed(self, connector, reason):
????????self.errback(reason)

既然clientConnectionFailed已經(jīng)接收到一個(gè)包含錯(cuò)誤信息的Failure 對(duì)象,我們把它傳給errback 就可以了.其他的改變就很小了,我們就略去不講了,你可以在不啟動(dòng)server 的情況下測(cè)試client 3.1:

python twisted-client-3/get-poetry-1.py 10004

你會(huì)看到下面的一些輸出:

Poem failed: [Failure instance: Traceback (failure with no frames): : Connection was refused by other side: 111: Connection refused.
]
輸出是從我們的poem_failed errback 輸出的,在這種情況下,twisted 僅僅傳遞給我們一個(gè)Exception 而不是拋出,所以我們?cè)谶@里不會(huì)得到一個(gè)traceback.但是一個(gè)traceback 不是必須的,因?yàn)檫@個(gè)地方不是一個(gè)bug.只是twisted 告訴我們,我們不能連接到那個(gè)地址.

Summary

下面是我們從第六部分學(xué)到的:

????我們?yōu)閠wisted程序?qū)懙腶pi必須是異步的
????我們不能將同步的代碼和異步的代碼混合
????在我們的代碼中不能不用callback,就像twisted 那樣
????我們不得不用callback去處理錯(cuò)誤
????是不是意味著我們用twisted 寫(xiě)的每一個(gè)api都要包含callback和errback 兩個(gè)參數(shù)?這樣聽(tīng)起來(lái)可不是太美好.幸運(yùn)的是twisted 已經(jīng)用一個(gè)抽象把這兩個(gè)參數(shù)消除掉了并帶來(lái)一些新的特性.我們將在第七部分講到.

?

總結(jié)

以上是生活随笔為你收集整理的twisted系列教程六–继续重构twisted poetry client的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。