Skip to content

Commit 31ff4c1

Browse files
committed
Add async python chapter
1 parent cb3244d commit 31ff4c1

File tree

3 files changed

+608
-0
lines changed

3 files changed

+608
-0
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@
4040
- [Функции](./dev/python/functions.md)
4141
- [Декораторы и functools](./dev/python/decorators.md)
4242
- [Классы](./dev/python/classes.md)
43+
- [Итераторы, генераторы и корутины](./dev/python/async.md)
4344

4445
# Учимся у других
4546

4647
- [Коды и скрипты](./examples/intro.md)
4748
- [Уравнение огибающей Капчинского-Владимирского для пучка заряженных частиц](./examples/kenv.md)
4849
- [Оптимизация огибающей пучка заряженных частиц с помощью генетического алгоритма](./examples/envelope-optimize.md)
4950
- [Релятивистская разностная схема для расчета динамики частиц в сложных электрических и магнитных полях](./examples/redpic.md)
51+
- [Async API для кинотеатра](./examples/async-api.md)
5052

5153
- [Литература и статьи](./resume/literature.md)
5254

src/dev/python/async.md

Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
# Асинхронное API
2+
3+
## Введение в асинхронное программирование
4+
5+
Асинхронное программирование стало неотъемлемой частью современной Python-разработки и продолжает набирать популярность среди веб-разработчиков.
6+
7+
## Основные темы
8+
9+
### Итераторы, генераторы и корутины
10+
11+
#### Итераторы
12+
13+
Итераторы - фундаментальная концепция Python, которую разработчики используют ежедневно, часто не задумываясь об их работе. Любая коллекция в Python (списки, словари, множества, строки, файлы) является итерабельной.
14+
15+
**Реализация аналога функции range():**
16+
17+
```python
18+
class Range:
19+
def __init__(self, stop_value: int):
20+
self.current = -1
21+
self.stop_value = stop_value - 1
22+
23+
def __iter__(self):
24+
return RangeIterator(self)
25+
26+
class RangeIterator:
27+
def __init__(self, container):
28+
self.container = container
29+
30+
def __next__(self):
31+
if self.container.current < self.container.stop_value:
32+
self.container.current += 1
33+
return self.container.current
34+
raise StopIteration
35+
```
36+
37+
**Упрощенная версия:**
38+
39+
```python
40+
class Range2:
41+
def __init__(self, stop_value: int):
42+
self.current = -1
43+
self.stop_value = stop_value - 1
44+
45+
def __iter__(self):
46+
return self
47+
48+
def __next__(self):
49+
if self.current < self.stop_value:
50+
self.current += 1
51+
return self.current
52+
raise StopIteration
53+
```
54+
55+
**Как работает цикл for под капотом:**
56+
57+
```python
58+
iterable = Range2(5)
59+
iterator = iter(iterable)
60+
61+
while True:
62+
try:
63+
value = next(iterator)
64+
print(value)
65+
except StopIteration:
66+
break
67+
```
68+
69+
#### Генераторы
70+
71+
Генераторы работают на принципе запоминания контекста выполнения функции с помощью ключевого слова `yield`.
72+
73+
**Простой пример генератора:**
74+
75+
```python
76+
def simple_generator():
77+
yield 1
78+
yield 2
79+
return 3
80+
81+
gen = simple_generator()
82+
print(next(gen)) # 1
83+
print(next(gen)) # 2
84+
print(next(gen)) # StopIteration: 3
85+
```
86+
87+
**Генераторные выражения:**
88+
89+
```python
90+
gen_exp = (x for x in range(100000))
91+
print(gen_exp) # <generator object <genexpr> at 0x...>
92+
```
93+
94+
**Синтаксический сахар yield from:**
95+
96+
```python
97+
numbers = [1, 2, 3]
98+
99+
# Стандартный подход
100+
def func():
101+
for item in numbers:
102+
yield item
103+
104+
# Упрощенный подход
105+
def func():
106+
yield from numbers
107+
```
108+
109+
#### Корутины
110+
111+
Корутины - основные строительные блоки асинхронного программирования, появившиеся как решение проблемы GIL (Global Interpreter Lock).
112+
113+
**Пример корутины для финансовых расчетов:**
114+
115+
```python
116+
import math
117+
118+
def cash_return_coro(percent: float, years: int) -> float:
119+
value = math.pow(1 + percent / 100, years)
120+
while True:
121+
try:
122+
deposit = (yield)
123+
yield round(deposit * value, 2)
124+
except GeneratorExit:
125+
print('Выход из корутины')
126+
raise
127+
128+
# Использование
129+
coro = cash_return_coro(5, 5)
130+
next(coro)
131+
values = [1000, 2000, 5000, 10000, 100000]
132+
for item in values:
133+
print(coro.send(item))
134+
next(coro)
135+
coro.close()
136+
```
137+
138+
### Асинхронность в Python и asyncio
139+
140+
#### Типы задач
141+
142+
- **CPU bound-задачи** - интенсивное использование процессора (математические модели, нейросети, рендеринг)
143+
- **I/O bound-задачи** - основная работа с вводом/выводом (файловая система, сеть)
144+
- **Memory bound-задачи** - интенсивная работа с оперативной памятью
145+
146+
#### Проблема блокирующих операций
147+
148+
```python
149+
import requests
150+
151+
def do_some_logic(data):
152+
pass
153+
154+
def save_to_database(data):
155+
pass
156+
157+
# Блокирующий код
158+
data = requests.get('https://data.aggregator.com/films')
159+
processed_data = do_some_logic(data)
160+
save_to_database(data)
161+
```
162+
163+
#### Event Loop - сердце асинхронных программ
164+
165+
**Базовая реализация планировщика:**
166+
167+
```python
168+
import logging
169+
from typing import Generator
170+
from queue import Queue
171+
172+
class Scheduler:
173+
def __init__(self):
174+
self.ready = Queue()
175+
self.task_map = {}
176+
177+
def add_task(self, coroutine: Generator) -> int:
178+
new_task = Task(coroutine)
179+
self.task_map[new_task.tid] = new_task
180+
self.schedule(new_task)
181+
return new_task.tid
182+
183+
def exit(self, task: Task):
184+
del self.task_map[task.tid]
185+
186+
def schedule(self, task: Task):
187+
self.ready.put(task)
188+
189+
def _run_once(self):
190+
task = self.ready.get()
191+
try:
192+
result = task.run()
193+
except StopIteration:
194+
self.exit(task)
195+
return
196+
self.schedule(task)
197+
198+
def event_loop(self):
199+
while self.task_map:
200+
self._run_once()
201+
```
202+
203+
**Реализация задачи (Task):**
204+
205+
```python
206+
import types
207+
from typing import Generator, Union
208+
209+
class Task:
210+
task_id = 0
211+
212+
def __init__(self, target: Generator):
213+
Task.task_id += 1
214+
self.tid = Task.task_id
215+
self.target = target
216+
self.sendval = None
217+
self.stack = []
218+
219+
def run(self):
220+
while True:
221+
try:
222+
result = self.target.send(self.sendval)
223+
224+
if isinstance(result, types.GeneratorType):
225+
self.stack.append(self.target)
226+
self.sendval = None
227+
self.target = result
228+
else:
229+
if not self.stack:
230+
return
231+
self.sendval = result
232+
self.target = self.stack.pop()
233+
234+
except StopIteration:
235+
if not self.stack:
236+
raise
237+
self.sendval = None
238+
self.target = self.stack.pop()
239+
```
240+
241+
#### Asyncio
242+
243+
С версии Python 3.5 появился синтаксис async/await для нативных корутин.
244+
245+
**Простая программа с asyncio:**
246+
247+
```python
248+
import random
249+
import asyncio
250+
251+
async def func():
252+
r = random.random()
253+
await asyncio.sleep(r)
254+
return r
255+
256+
async def value():
257+
result = await func()
258+
print(result)
259+
260+
if __name__ == '__main__':
261+
loop = asyncio.get_event_loop()
262+
loop.run_until_complete(value())
263+
loop.close()
264+
```
265+
266+
**Основные функции asyncio:**
267+
268+
- `gather` - одновременное выполнение корутин
269+
- `sleep` - приостановка выполнения
270+
- `wait` / `wait_for` - ожидание выполнения корутин
271+
272+
**Основные функции event_loop:**
273+
274+
- `get_event_loop` - получение объекта цикла событий
275+
- `run_until_complete` / `run` - запуск асинхронных функций
276+
- `shutdown_asyncgens` - корректное завершение
277+
- `call_soon` - планирование выполнения
278+
279+
### Асинхронные фреймворки
280+
281+
#### Twisted
282+
283+
Один из старейших асинхронных фреймворков с собственной реализацией event-loop.
284+
285+
**Основные концепции:**
286+
287+
1. **Protocol** - описание получения и отправки данных
288+
2. **Factory** - управление созданием объектов протокола
289+
3. **Reactor** - собственная реализация event-loop
290+
4. **Deferred-объекты** - цепочки обратных вызовов
291+
292+
**Пример Deferred-объекта:**
293+
294+
```python
295+
from twisted.internet import defer
296+
297+
def toint(data):
298+
return int(data)
299+
300+
def increment_number(data):
301+
return data + 1
302+
303+
def print_result(data):
304+
print(data)
305+
306+
def handleFailure(f):
307+
print("OOPS!")
308+
309+
def get_deferred():
310+
d = defer.Deferred()
311+
return d.addCallbacks(toint, handleFailure)\
312+
.addCallbacks(increment_number, handleFailure)\
313+
.addCallback(print_result)
314+
```
315+
316+
#### Aiohttp
317+
318+
Асинхронные HTTP-клиент и сервер, построенные поверх asyncio.
319+
320+
**Пример приложения:**
321+
322+
```python
323+
import aiohttp
324+
from aiohttp import web
325+
326+
async def get_phrase():
327+
async with aiohttp.ClientSession() as session:
328+
async with session.get('https://fish-text.ru/get',
329+
params={'type': 'title'}) as response:
330+
result = await response.json(content_type='text/html; charset=utf-8')
331+
return result.get('text')
332+
333+
async def index_handler(request):
334+
return web.Response(text=await get_phrase())
335+
336+
async def response_signal(request, response):
337+
response.text = response.text.upper()
338+
return response
339+
340+
async def make_app():
341+
app = web.Application()
342+
app.on_response_prepare.append(response_signal)
343+
app.add_routes([web.get('/', index_handler)])
344+
return app
345+
346+
web.run_app(make_app())
347+
```
348+
349+
#### FastAPI
350+
351+
Современный фреймворк для быстрой разработки API, построенный на Starlette и Pydantic.
352+
353+
**Простой пример API:**
354+
355+
```python
356+
from fastapi import FastAPI
357+
from pydantic import BaseModel, Field
358+
from typing import Optional
359+
360+
app = FastAPI(title="Простые математические операции")
361+
362+
class Add(BaseModel):
363+
first_number: int = Field(title='Первое слагаемое')
364+
second_number: Optional[int] = Field(title='Второе слагаемое')
365+
366+
class Result(BaseModel):
367+
result: int = Field(title='Результат')
368+
369+
@app.post("/add", response_model=Result)
370+
async def create_item(item: Add):
371+
return {
372+
'result': item.first_number + (item.second_number or 1)
373+
}
374+
```
375+
376+
## Заключение
377+
378+
Вы познакомились с основами асинхронного программирования в Python, изучили ключевые концепции итераторов, генераторов и корутин, освоили работу с asyncio и популярными асинхронными фреймворками.

0 commit comments

Comments
 (0)