asyncio Python

Содержание
Введение
Простейший пример
HTTP запросы
Похожие статьи

Введение

Установка

python -m venv venv
source venv/Scripts/activate
touch requirements.txt
vi requirements.txt

python -m pip install --upgrade pip
python -m pip install -r requirements.txt

Пример

import time def brew_coffee(): print("Start brew_coffee()") time.sleep(3) print("End brew_coffee()") return "Coffee ready" def toast_bagel(): print("Start toast_bagel()") time.sleep(2) print("End toast_bagel()") return "Bagel ready" def main(): start_time = time.time() result_coffee = brew_coffee() result_bagel = toast_bagel() end_time = time.time() elapsed_time = end_time - start_time print(f"Result of brew_coffee is: {result_coffee}") print(f"Result of toast_bagel is: {result_bagel}") print(f"Elapsed time: {elapsed_time}") if __name__ == "__main__": main()

Start brew_coffee() End brew_coffee() Start toast_bagel() End toast_bagel() Result of brew_coffee is: Coffee ready Result of toast_bagel is: Bagel ready Elapsed time: 5.0012452602386475 Process finished with exit code 0

import asyncio import time async def brew_coffee(): print("Start brew_coffee()") time.sleep(3) print("End brew_coffee()") return "Coffee ready" async def toast_bagel(): print("Start toast_bagel()") time.sleep(2) print("End toast_bagel()") return "Bagel ready" def main(): start_time = time.time() result_coffee = brew_coffee() result_bagel = toast_bagel() end_time = time.time() elapsed_time = end_time - start_time print(f"Result of brew_coffee is: {result_coffee}") print(f"Result of toast_bagel is: {result_bagel}") print(f"Elapsed time: {elapsed_time}") if __name__ == "__main__": main()

Result of brew_coffee is: <coroutine object brew_coffee at 0x00000202D448D240> Result of toast_bagel is: <coroutine object toast_bagel at 0x00000202D448E5C0> Elapsed time: 0.0 C:\Users\Andrei\py\async_ex.py:36: RuntimeWarning: coroutine 'brew_coffee' was never awaited main() RuntimeWarning: Enable tracemalloc to get the object allocation traceback C:\Users\Andrei\py\async_ex.py:36: RuntimeWarning: coroutine 'toast_bagel' was never awaited main() RuntimeWarning: Enable tracemalloc to get the object allocation traceback

Используем asyncio.gather() чтобы запустить задания асинхронно

import asyncio import time async def brew_coffee(): print("Start brew_coffee()") # time.sleep is not awaitable function await asyncio.sleep(3) print("End brew_coffee()") return "Coffee ready" async def toast_bagel(): print("Start toast_bagel()") await asyncio.sleep(2) print("End toast_bagel()") return "Bagel ready" async def main(): start_time = time.time() batch = asyncio.gather(brew_coffee(), toast_bagel()) result_coffee, result_bagel = await batch end_time = time.time() elapsed_time = end_time - start_time print(f"Result of brew_coffee is: {result_coffee}") print(f"Result of toast_bagel is: {result_bagel}") print(f"Elapsed time: {elapsed_time}") if __name__ == "__main__": asyncio.run(main())

Start brew_coffee() Start toast_bagel() End toast_bagel() End brew_coffee() Result of brew_coffee is: Coffee ready Result of toast_bagel is: Bagel ready Elapsed time: 3.0193281173706055

Альтернативный способ

async def main(): start_time = time.time() # batch = asyncio.gather(brew_coffee(), toast_bagel()) # result_coffee, result_bagel = await batch coffee_task = asyncio.create_task(brew_coffee()) toast_task = asyncio.create_task(toast_bagel()) result_coffee = await coffee_task result_bagel = await toast_task end_time = time.time() elapsed_time = end_time - start_time print(f"Result of brew_coffee is: {result_coffee}") print(f"Result of toast_bagel is: {result_bagel}") print(f"Elapsed time: {elapsed_time:.2f} seconds")

HTTP запросы

В файле req_http.py сохраним библиотеку из двух функций для выполнения синхронных и асинхронных запросов с помощью requests

# req_http.py import asyncio import requests # A few handy JSON types JSON = int | str | float | bool | None | dict[str, "JSON"] | list["JSON"] JSONObject = dict[str, JSON] JSONList = list[JSON] def http_get_sync(url: str) -> JSONObject: response = requests.get(url) return response.json() async def http_get(url: str) -> JSONObject: return await asyncio.to_thread(http_get_sync, url)

В дальнейших примерах будем использовать сперва синхронную функцию http_get_sync(), а затем и асинхронную http_get()

Получим с pokeapi.co имя случайного покемона. Сперва синхронным способом

from random import randint from req_http import http_get_sync # The highest Pokemon id MAX_POKEMON = 898 def get_random_pokemon_name_sync() -> str: pokemon_id = randint(1, MAX_POKEMON) pokemon_url = f"https://pokeapi.co/api/v2/pokemon/{pokemon_id}" pokemon = http_get_sync(pokemon_url) return str(pokemon["name"]) def main() -> None: pokemon_name = get_random_pokemon_name_sync() print(pokemon_name) if __name__ == "__main__": main()

swanna

Асинхронный вариант тех же действий

import asyncio from random import randint from req_http import http_get # The highest Pokemon id MAX_POKEMON = 898 async def get_random_pokemon_name() -> str: pokemon_id = randint(1, MAX_POKEMON) pokemon_url = f"https://pokeapi.co/api/v2/pokemon/{pokemon_id}" pokemon = await http_get(pokemon_url) return str(pokemon["name"]) async def main() -> None: pokemon_name = await get_random_pokemon_name() print(pokemon_name) if __name__ == "__main__": asyncio.run(main())

bronzong

В данном примере делается всего один запрос, поэтому разницу увидеть нельзя.

Выполним по 20 запросов. Оставим асинхронный синтаксис, но в первом случае просто запустим цикл for … in а во втором случае используем asyncio.gather()

В предыдущем примере у нас было два разных именованных задания, поэтому мы перечисляли их явно

batch = asyncio.gather(brew_coffee(), toast_bagel())

Сейчас одно задание нужно асинхронно запустить 20 раз поэтому мы воспользуемся генератором списка

import asyncio from random import randint from time import perf_counter from req_http import http_get # The highest Pokemon id MAX_POKEMON = 898 async def get_random_pokemon_name() -> str: pokemon_id = randint(1, MAX_POKEMON) pokemon_url = f"https://pokeapi.co/api/v2/pokemon/{pokemon_id}" pokemon = await http_get(pokemon_url) return str(pokemon["name"]) async def main() -> None: # 20 calls without real async time_before = perf_counter() for _ in range(20): pokemon_name = await get_random_pokemon_name() print(pokemon_name) print(f"Total time (synchronous): {perf_counter() - time_before:.2f}.") # 20 calls with async time_before = perf_counter() result = await asyncio.gather(*[get_random_pokemon_name() for _ in range(20)]) print(result) print(f"Total time (asynchronous): {perf_counter() - time_before:.2f}.") if __name__ == "__main__": asyncio.run(main())

cleffa timburr gothita wynaut wynaut trumbeak audino togedemaru dedenne necrozma cramorant gulpin registeel lileep houndoom luvdisc tympole dratini braixen floatzel Total time (synchronous): 9.47. ['dhelmise', 'articuno', 'ferroseed', 'rillaboom', 'vanillite', 'darkrai', 'hatterene', 'machamp', 'charjabug', 'dreepy', 'pineco', 'ralts', 'liepard', 'greedent', 'ferrothorn', 'urshifu-single-strike', 'combee', 'taillow', 'sandaconda', 'falinks'] Total time (asynchronous): 2.53.

Выигрыш во времени составил почти семь секунд

Article author: Andrei Olegovich

Похожие статьи
FastAPI
list comprehension: Абстракция списка
Python
Pydantic
Пример API с БД
Циклы
multiprocessing

Search this site

Subscribe to Telegram channel @aofeed to follow new articles and updates to old ones

Go to channel

@aofeed

Ask a question in the Telegram group

@aofeedchat

Contacts and cooperation:
I recommend our hosting beget.ru
Write to info@urn.su, info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: