Если какой-то метод использует что-то асинхронное, оно также должно быть явно определено как асинхронное.Это основная идея asyncio
: заставить вас писать код так, чтобы вы всегда знали, может ли какой-нибудь произвольный метод сделать что-то асинхронное.
В вашем фрагменте вы хотите сделать асинхронную вещь (bar
I / O) метод внутренней синхронизации __init__
и asyncio
запрещает это.Вы должны выполнить _run_coro
async и инициализировать Foo
асинхронно, например, , используя __await__
метод:
import asyncio
class Foo:
def __await__(self):
return self._run_coro().__await__()
async def _run_coro(self): # real async initializer
async def init():
await asyncio.sleep(1) # bar I/O
self.bar = 123
await init()
return self
async def spam(self):
return await asyncio.sleep(1) # I/O op
async def main():
foo = await Foo()
await foo.spam()
asyncio.run(main()) # instead of two lines in Python 3.7+
Возможно, вам будет интересно прочитать этот ответ чтобы лучше понять, как работает asyncio
и как с этим справиться.
Upd:
s = self.init_async_session()
try:
s.send(None)
Не делайте таких вещей: метод генератора - это только детали реализации в отношении сопрограмм.Вы можете предсказать, как сопрограмма отреагирует на вызов метода .send()
, и вы можете положиться на это поведение.
Если вы хотите выполнить сопрограмму, используйте await
, если вы хотите запустить ее "в фоновом режиме", используйте задача или другие функции из asyncio
документ .
Какой будет правильный способ инициализации self.async_session
Когда дело доходит до aiohttp.ClientSession
, оно должно быть не только создано, но и должным образом закрыто.Лучший способ сделать это - использовать асинхронный менеджер контекста, как показано в aiohttp
doc .
Если вы хотите скрыть эту операцию внутри Foo
, вы также можете сделать его асинхронным менеджером.Полный пример:
import aiohttp
class Foo:
async def __aenter__(self):
self._session = aiohttp.ClientSession()
await self._session.__aenter__()
return self
async def __aexit__(self, *args):
await self._session.__aexit__(*args)
async def spam(self):
url = 'http://httpbin.org/delay/1'
resp = await self._session.get(url)
text = await resp.text()
print(text)
async def main():
async with Foo() as foo:
await foo.spam()
asyncio.run(main())
Upd2:
Вы можете комбинировать способы инициализации / закрытия объекта сверху для достижения желаемого результата.Пока вы помните, что обе операции асинхронны и, следовательно, их следует ожидать, все должно быть в порядке.
Еще один возможный способ:
import asyncio
import aiohttp
class Foo:
def __await__(self):
return self._init().__await__()
async def _init(self):
self._session = aiohttp.ClientSession()
await self._session.__aenter__()
return self
async def close(self):
await self._session.__aexit__(None, None, None)
async def spam(self):
url = 'http://httpbin.org/delay/1'
resp = await self._session.get(url)
text = await resp.text()
print(text)
async def main():
foo = await Foo()
try:
await foo.spam()
finally:
await foo.close()
asyncio.run(main())