Зачем нужны Асинхронные итераторы в Python?
Потихоньку разбираясь с библиотекой asyncio наткнулся на "асинхронные" итераторы. Посмотрев примеры написал простой итератор и точно такой же, но "асинхронный". После запуска скрипта я Не обнаружил никакой разницы между асинхронным и обычным итератором.
Вывод:
async
wait for Andrey 2c
wait for Alex 2c
wait for Artem 2c
Time: 6.04335618019104
No async
wait for Andrey 2c
wait for Alex 2c
wait for Artem 2c
Time: 6.024480581283569
import asyncio
import time
class Iterator:
def __init__(self, delay, people: list[str]):
self._people = people
self.delay = delay
self._i = 0
def __iter__(self):
return self
def __next__(self):
if self._i >= len(self._people):
raise StopIteration
human = self._people[self._i]
self._i += 1
time.sleep(self.delay)
return f"wait for {human} {self.delay}c"
class Crowd:
def __init__(self, delay, people: list[str]):
self._people = people
self.delay = delay
self._i = 0
def __aiter__(self):
return self
async def __anext__(self):
if self._i >= len(self._people):
raise StopAsyncIteration
human = self._people[self._i]
self._i += 1
await asyncio.sleep(self.delay)
return f"wait for {human} {self.delay}c"
async def amain():
crowd = Crowd(2, ["Andrey", "Alex", "Artem"])
async for i in crowd:
print(i)
def main():
iterr = Iterator(2, ["Andrey", "Alex", "Artem"])
for i in iterr:
print(i)
begin = time.time()
print("async")
asyncio.run(amain())
print("Time:", time.time() - begin)
begin = time.time()
print("\nNo async")
main()
print("Time:", time.time() - begin)
В моём понимании при первой асинхронной итерации должен вызваться метод __anext__ и дойдя до await asyncio.sleep(delay) должна происходить следующая итерация, так как asyncio.sleep(delay) передаёт управление следующим task-ам (как в примере ниже), но этого не происходит.
Вопрос: зачем тогда нужен асинхронный итератор, если обычный итератор даёт такие же результаты по времени?
async def sleep(i):
print(f"({i}) start")
await asyncio.sleep(2)
async def main():
tasks = [sleep(i) for i in range(1, 6)]
await asyncio.gather(*tasks)
asyncio.run(main())
Ответы (1 шт):
Короткий пример с вашим же кодом - асинхронное одновременное выполнение двадцати ваших итераторов за тоже самое время, что выполняется один итератор:
# asyncio.run(amain())
asyncio.run(asyncio.gather(*[amain() for _ in range(20)]))
Если у вас остальной код будет асинхронный, то использование асинхронных конструкций позволит выполнять код [почти] параллельно, причём это возможно даже без использования дополнительных потоков, даже в одном потоке. Пока в одном месте производится асинхронное ожидание ввода-вывода или работает асинхронный sleep, в другом месте программы может выполняться другой код. В моём примере параллельно выполняются 20 веток кода, поскольку код в основном спит всё это время, это практически не тратит ресурсы процессора.