Каков правильный способ выхода из потока?

У меня есть объект Connection который используется для хранения потоков чтения и записи asyncio соединений:

 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 from каждому разговору на read и write . Я попробовал 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> 

Если я буду называть read и write из нескольких мест, я бы предпочел не повторять yield from s каждый раз; вместо того, чтобы держать их внутри Connection . Моя конечная цель – сократить мою функцию new_conection :

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

One Solution collect form web for “Каков правильный способ выхода из потока?”

Поскольку StreamReader.read является сопрограммой , ваши единственные возможности для ее вызова: a) обернуть ее в Task или Future и запустить ее через цикл событий; b) await ее из сопрограммы, определенной с помощью async def , или c) используя yield from с ним из сопрограммы, определенной как функция, украшенная @asyncio.coroutine .

Поскольку Connection.read вызывается из цикла событий (через coroutine new_connection ), вы не можете повторно использовать этот цикл событий для запуска Task или Future для StreamReader.read : циклы событий не могут быть запущены, пока они уже запущены . Вам нужно либо остановить цикл событий (катастрофический, и, возможно, не сделать это правильно), либо создать новый цикл событий (беспорядочный и победить цель использования сопрограмм). Ни один из них не нужен, поэтому Connection.read должен быть сопрограммой или функцией async .

Остальные два варианта ( await в async def coroutine или yield from функции @asyncio.coroutine ) в основном эквивалентны. Единственное отличие заключается в том, что async def и await были добавлены в Python 3.5 , поэтому для 3.4, используя yield from и @asyncio.coroutine это единственный вариант (сопрограммы и asyncio не существовали до 3.4, поэтому другие версии не имеют значения). Лично я предпочитаю использовать async def и await , потому что определение сопрограммы с async def является более чистым и понятным, чем с декоратором.

Вкратце: если Connection.read и new_connection являются сопрограммами (используя либо декоратор, либо ключевое слово async ), и используйте await (или yield from ) при вызове других await conn.read(4) в new_connection и await self.__in.read(n_bytes) в Connection.read ).

Python - лучший язык программирования в мире.