2017-08-26 75 views
25

我有用於包含讀和寫的asyncio連接流的Connection對象:從流中產生的正確方法是什麼?

class Connection(object): 

    def __init__(self, stream_in, stream_out): 
     object.__init__(self) 

     self.__in = stream_in 
     self.__out = stream_out 

    def read(self, n_bytes : int = -1): 
     return self.__in.read(n_bytes) 

    def write(self, bytes_ : bytes): 
     self.__out.write(bytes_) 
     yield from self.__out.drain() 

在服務器端,connected創建Connection對象每一個客戶端連接時間,然後讀出4個字節。

@asyncio.coroutine 
def new_conection(stream_in, stream_out): 
    conn = Connection(stream_in, stream_out) 
    data = yield from conn.read(4) 
    print(data) 

而在客戶端,寫出4個字節。

@asyncio.coroutine 
def client(loop): 
    ... 
    conn = Connection(stream_in, stream_out) 
    yield from conn.write(b'test') 

這個作品幾乎符合市場預期,但我必須yield fromreadwrite通話。我試過yield from從內Connection荷蘭國際集團:

def read(self, n_bytes : int = -1): 
    data = yield from self.__in.read(n_bytes) 
    return data 

但是比起獲取數據,我得到這樣

<generator object StreamReader.read at 0x1109983b8> 

如果我打電話readwrite來自多個地方的輸出,我寧願不每次重複yield from;寧可將它們放在Connection之內。我的最終目標是我new_conection功能砍倒在此:

@asyncio.coroutine 
def new_conection(stream_in, stream_out): 
    conn = Connection(stream_in, stream_out) 
    print(conn.read(4)) 
+0

爲什麼你必須屈服?如果你不從conn.read(4)產生,它在我看來就像它只是返回一個字節對象。這是你在這裏尋找什麼? – RageCage

+0

@RageCage:如果沒有''yield'',conn.read(4)'仍然返回一個生成器:'<生成器對象Connection.read at 0x1019262b0>' –

+0

對不起,我應該澄清;如果你不從conn.read()(單行版本)的第一次迭代中產生什麼結果? – RageCage

回答

6

因爲StreamReader.read is a coroutine,您的用於調用它的唯一選項是a)在TaskFuture它包裹並運行通過一個事件循環,B)await從與async def定義協程荷蘭國際集團,或c)使用yield from與它從一個協程定義爲用@asyncio.coroutine裝飾的函數。

由於Connection.read從事件循環中調用(通過協程new_connection),你不能重複使用事件循環運行StreamReader.read一個TaskFutureevent loops can't be started while they're already running。你要麼必須stop the event loop(災難性的,可能不可能做到正確)或create a new event loop(凌亂和挫敗使用協程的目的)。這兩種都不可取,因此Connection.read需要是協程或async函數。

其他兩個選項(在協程async defawaityield from一個@asyncio.coroutine -decorated函數)大部分是等價的。唯一的區別是async def and await were added in Python 3.5,因此對於3.4,使用yield from@asyncio.coroutine是唯一選項(協同和asyncio在3.4之前不存在,所以其他版本是不相關的)。就個人而言,我更喜歡使用async defawait,因爲與async def一起定義協程比使用裝飾器更清晰和更清晰。

簡言之:具有Connection.readnew_connection是協同程序(使用裝飾或async關鍵字),並使用await(或yield from)調用其他協同程序(await conn.read(4)new_connection,和await self.__in.read(n_bytes)Connection.read)時。

+1

啊,非常好的答案Mego!這清楚地由知道他們在談論什麼的人寫出。我從閱讀中學到了很多東西。 +1 –

1

我發現StreamReader source code的塊上線620實際上是功能的使用的一個很好的例子。

在我以前的答案,我忽略了一個事實,即​​不僅是協程(這是我應該早就知道考慮到這是從asyncio模塊XD),但它產生於行的結果。所以它實際上是一個發電機,你需要從中產生效益。

借款從源代碼這個循環中,你讀的功能應該是這個樣子:

def read(self, n_bytes : int = -1): 
    data = bytearray() #or whatever object you are looking for 
    while 1: 
     block = yield from self.__in.read(n_bytes) 
     if not block: 
      break 
     data += block 
    return data 

因爲​​是一臺發電機,你必須繼續從它產生,直到它得到一個空的結果信號閱讀結束。現在你的讀函數應該返回數據而不是生成器。您不必從conn.read()這個版本中產生。

+0

完全按照您提供的函數使用函數,我仍然收到一個生成器對象('Connection.read')。 –

+0

你還在從conn.read調用中產生嗎?嘗試打印數據並在讀取函數中鍵入(數據)以查看返回前的內容。 – RageCage

+0

不,我刪除了它,而是嘗試了'data = conn.read(4)'。它是一個發電機。 –

相關問題