🚀 **КАК РАБОТАЕТ asyncio.gather И ЧЕМ ОТЛИЧАЕТСЯ ОТ asyncio.wait?**
Представь, что ты — шеф-повар, которому нужно приготовить 5 блюд одновременно. Если делать их по очереди — гости уйдут голодными. А если запустить все процессы параллельно? Вот тут и приходят на помощь asyncio.gather и asyncio.wait — твои асинхронные менеджеры! 🍽️
Давай разберемся, в чем их сила и когда что использовать. Поехали! 🏁
---
### 🔹 asyncio.gather() — «Собери всё и верни порядок»
Этот инструмент запускает несколько корутин (асинхронных функций) одновременно и ждет, пока все они завершатся. Результаты возвращаются в том же порядке, в котором ты передал задачи. Как официант, который собирает все заказы и приносит их на одном подносе — сначала суп, потом горячее, потом десерт. 🍲➡️🥩➡️🍰
Пример:
Важно: Если хоть одна задача упадет с ошибкой —
---
### 🔹 asyncio.wait() — «Гибкий контроль»
А вот
Пример:
Фишки wait:
•
•
•
---
### 🆚 Главные отличия
| Критерий | gather | wait |
|----------|--------|------|
| Возвращает | список результатов в порядке задач | два набора: done и pending |
| Обработка ошибок | прерывается при первой ошибке (если не return_exceptions) | можно гибко обрабатывать |
| Гибкость | простая, «все или ничего» | высокая: можно обрабатывать по мере готовности |
| Тип аргументов | отдельные корутины или список через * | итерабель (список, множество) корутин/фьючерсов |
---
### 🎯 Когда что использовать?
gather — твой выбор, если:
• Нужно запустить несколько независимых задач и получить все результаты сразу.
• Порядок важен.
• Ты хочешь простой и читаемый код.
wait — бери, если:
• Нужно обрабатывать результаты по мере поступления (например, для стриминга).
• Хочешь контролировать, когда остановиться (первый успех, первая ошибка).
• Работаешь с динамическим списком задач.
---
### 💡 Совет сеньора
Не усложняй! Для 80% случаев
Запомни: асинхронность — это не про магию, а про эффективное ожидание. Используй правильный инструмент — и твой код будет летать! 🚀
#Python #Asyncio #Senior
Представь, что ты — шеф-повар, которому нужно приготовить 5 блюд одновременно. Если делать их по очереди — гости уйдут голодными. А если запустить все процессы параллельно? Вот тут и приходят на помощь asyncio.gather и asyncio.wait — твои асинхронные менеджеры! 🍽️
Давай разберемся, в чем их сила и когда что использовать. Поехали! 🏁
---
### 🔹 asyncio.gather() — «Собери всё и верни порядок»
Этот инструмент запускает несколько корутин (асинхронных функций) одновременно и ждет, пока все они завершатся. Результаты возвращаются в том же порядке, в котором ты передал задачи. Как официант, который собирает все заказы и приносит их на одном подносе — сначала суп, потом горячее, потом десерт. 🍲➡️🥩➡️🍰
Пример:
import asyncio
async def fetch_data(n):
await asyncio.sleep(n)
return f"Данные {n}"
async def main():
results = await asyncio.gather(
fetch_data(2),
fetch_data(1),
fetch_data(3)
)
print(results) # ['Данные 2', 'Данные 1', 'Данные 3']
asyncio.run(main())
Важно: Если хоть одна задача упадет с ошибкой —
gather поднимет исключение сразу. Чтобы этого избежать, используй параметр return_exceptions=True — тогда ошибки вернутся как обычные объекты, а не прервут весь процесс. 🛡️---
### 🔹 asyncio.wait() — «Гибкий контроль»
А вот
wait — это уже менеджер-стратег. Он не просто ждет все задачи, а дает тебе наборы: done (завершенные) и pending (еще выполняющиеся). Ты можешь обрабатывать результаты по мере их готовности, а не ждать всех! 🎯Пример:
import asyncio
async def task(name, delay):
await asyncio.sleep(delay)
return f"{name} готов"
async def main():
tasks = [
task("A", 2),
task("B", 1),
task("C", 3)
]
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
for t in done:
print(t.result()) # "B готов" — первый!
asyncio.run(main())
Фишки wait:
•
return_when=asyncio.FIRST_COMPLETED — ждем первую завершенную задачу.•
return_when=asyncio.ALL_COMPLETED — ждем все (как gather).•
return_when=FIRST_EXCEPTION — ждем первую ошибку.---
### 🆚 Главные отличия
| Критерий | gather | wait |
|----------|--------|------|
| Возвращает | список результатов в порядке задач | два набора: done и pending |
| Обработка ошибок | прерывается при первой ошибке (если не return_exceptions) | можно гибко обрабатывать |
| Гибкость | простая, «все или ничего» | высокая: можно обрабатывать по мере готовности |
| Тип аргументов | отдельные корутины или список через * | итерабель (список, множество) корутин/фьючерсов |
---
### 🎯 Когда что использовать?
gather — твой выбор, если:
• Нужно запустить несколько независимых задач и получить все результаты сразу.
• Порядок важен.
• Ты хочешь простой и читаемый код.
wait — бери, если:
• Нужно обрабатывать результаты по мере поступления (например, для стриминга).
• Хочешь контролировать, когда остановиться (первый успех, первая ошибка).
• Работаешь с динамическим списком задач.
---
### 💡 Совет сеньора
Не усложняй! Для 80% случаев
gather — идеальный выбор. Но когда тебе нужно управлять тайм-аутами, частичными результатами или отменой задач — смело используй wait в связке с asyncio.as_completed (это еще один мощный инструмент, но о нем в следующий раз 😉).Запомни: асинхронность — это не про магию, а про эффективное ожидание. Используй правильный инструмент — и твой код будет летать! 🚀
#Python #Asyncio #Senior
🚀 Сеньор, ты готов к вопросу про array и numpy?
Вот сидишь ты на собеседовании, тебе задают вопрос: «Как оптимизировать работу с большими списками чисел?»
Ты, как новичок, можешь начать мямлить про list comprehension. И это правильно, но для Senior этого МАЛО. Нужно показать глубину. Давай разберем, как прозвучать как профи.
---
1. Почему стандартный list — это тормоз?
Список в Python — это супергибкая штука. В один список можно засунуть число, строку, словарь и даже другой список. Но за эту гибкость мы платим ПРОИЗВОДИТЕЛЬНОСТЬЮ.
Каждый элемент в списке — это отдельный объект в памяти Python. Когда ты делаешь цикл
2. Первый шаг — array из модуля array
Модуль
Как это выглядит:
Плюсы:
- Экономит память (элементы хранятся компактно, как в C).
- Работает быстрее обычного списка в циклах.
Минусы:
- Все элементы должны быть одного типа.
- Ты все еще можешь делать только поэлементные операции. Хочешь умножить каждый элемент на 2? Нужен цикл.
3. Второй шаг — NumPy. Это БОМБА.
NumPy — это библиотека, написанная на C. Она дает нам
Главная фишка — векторизация. Это значит, что ты можешь делать операции над ВСЕМ массивом сразу, без циклов.
Смотри пример:
Почему это быстро?
NumPy не использует циклы Python. Внутри он вызывает оптимизированный код на C и FORTRAN, который работает с кусками памяти напрямую. Разница в скорости может быть в 50-100 раз!
4. Что еще умеет NumPy?
- Универсальные функции (ufunc):
- Индексация и срезы:
- Статистика:
5. Когда что использовать?
- Обычный list: когда данные разнотипные, и тебе нужна гибкость.
- array: когда данные одного типа, но операции простые (например, просто хранить).
- NumPy: когда нужно считать математику, статистику, работать с большими объемами данных (миллионы+ элементов).
6. Как блеснуть на собеседовании?
Скажи так:
«Для оптимизации работы с большими списками чисел я использую NumPy. Главное преимущество — векторизация. Вместо того чтобы писать цикл for, я оперирую целыми массивами. Это дает прирост скорости в десятки раз, потому что NumPy написан на C и использует оптимизированные алгоритмы работы с памятью. Для простых задач, где нужна только экономия памяти, можно использовать встроенный модуль array, но для серьезных вычислений — только NumPy.»
Звучит как сеньор, правда?
---
Итог:
-
-
-
Твой ход. Иди и готовься! 💪
#python #senior #numpy
Вот сидишь ты на собеседовании, тебе задают вопрос: «Как оптимизировать работу с большими списками чисел?»
Ты, как новичок, можешь начать мямлить про list comprehension. И это правильно, но для Senior этого МАЛО. Нужно показать глубину. Давай разберем, как прозвучать как профи.
---
1. Почему стандартный list — это тормоз?
Список в Python — это супергибкая штука. В один список можно засунуть число, строку, словарь и даже другой список. Но за эту гибкость мы платим ПРОИЗВОДИТЕЛЬНОСТЬЮ.
Каждый элемент в списке — это отдельный объект в памяти Python. Когда ты делаешь цикл
for по 10 миллионам чисел, Python каждый раз проверяет тип элемента, создает временные объекты, вызывает методы... Это ОЧЕНЬ медленно.2. Первый шаг — array из модуля array
Модуль
array — это встроенная в Python штука. Он создает список, где ВСЕ элементы одного типа (например, только целые числа).Как это выглядит:
from array import array
# Обычный список
my_list = [1, 2, 3, 4, 5]
# Массив из целых чисел (тип 'i')
my_array = array('i', [1, 2, 3, 4, 5])
Плюсы:
- Экономит память (элементы хранятся компактно, как в C).
- Работает быстрее обычного списка в циклах.
Минусы:
- Все элементы должны быть одного типа.
- Ты все еще можешь делать только поэлементные операции. Хочешь умножить каждый элемент на 2? Нужен цикл.
3. Второй шаг — NumPy. Это БОМБА.
NumPy — это библиотека, написанная на C. Она дает нам
ndarray (N-мерный массив).Главная фишка — векторизация. Это значит, что ты можешь делать операции над ВСЕМ массивом сразу, без циклов.
Смотри пример:
import numpy as np
# Обычный список
list_a = [1, 2, 3, 4, 5]
list_b = [10, 20, 30, 40, 50]
# Чтобы сложить поэлементно, нужен цикл
result_list = [a + b for a, b in zip(list_a, list_b)]
# А с NumPy:
arr_a = np.array([1, 2, 3, 4, 5])
arr_b = np.array([10, 20, 30, 40, 50])
result_arr = arr_a + arr_b # Просто! Быстро!
Почему это быстро?
NumPy не использует циклы Python. Внутри он вызывает оптимизированный код на C и FORTRAN, который работает с кусками памяти напрямую. Разница в скорости может быть в 50-100 раз!
4. Что еще умеет NumPy?
- Универсальные функции (ufunc):
np.sqrt(), np.sin(), np.exp() — они работают над всем массивом сразу.- Индексация и срезы:
arr[arr > 5] — выбрать все элементы больше 5.- Статистика:
arr.mean(), arr.sum(), arr.std().5. Когда что использовать?
- Обычный list: когда данные разнотипные, и тебе нужна гибкость.
- array: когда данные одного типа, но операции простые (например, просто хранить).
- NumPy: когда нужно считать математику, статистику, работать с большими объемами данных (миллионы+ элементов).
6. Как блеснуть на собеседовании?
Скажи так:
«Для оптимизации работы с большими списками чисел я использую NumPy. Главное преимущество — векторизация. Вместо того чтобы писать цикл for, я оперирую целыми массивами. Это дает прирост скорости в десятки раз, потому что NumPy написан на C и использует оптимизированные алгоритмы работы с памятью. Для простых задач, где нужна только экономия памяти, можно использовать встроенный модуль array, но для серьезных вычислений — только NumPy.»
Звучит как сеньор, правда?
---
Итог:
-
list — гибкий, но медленный.-
array — быстрее, экономит память.-
numpy — РАКЕТА для чисел.Твой ход. Иди и готовься! 💪
#python #senior #numpy
🚀 Ты на собеседовании на Senior Python Developer. Звучит вопрос: «Расскажи про контекстные менеджеры для работы с базами данных». Не мямли, не говори «ну, это такая штука, чтобы соединение закрывать». Покажи глубину. Поехали!
1. База: зачем они вообще нужны?
Представь: ты открываешь соединение с БД, делаешь запросы, а потом… забываешь его закрыть. Или происходит исключение, и соединение «виснет». Это утечка ресурсов, и на продакшене такое аукнется пулом занятых соединений. Контекстный менеджер (через
2. Как это выглядит в коде?
Самый простой способ — использовать готовый менеджер из библиотеки:
3. А если библиотека не поддерживает
Тогда ты пишешь свой контекстный менеджер. Это два магических метода:
4. Что важно для Senior?
Ты должен понимать:
• Транзакции: внутри
• Пул соединений: контекстный менеджер не обязан каждый раз создавать новое соединение. Он может брать готовое из пула (например,
• Вложенные менеджеры: один
• Исключения: в
5. Пример для продакшена (на
6. Главный совет для собеса:
Не просто расскажи про
Теперь ты готов. Иди и закрой эту тему 🔥
#python #senior #собеседование
1. База: зачем они вообще нужны?
Представь: ты открываешь соединение с БД, делаешь запросы, а потом… забываешь его закрыть. Или происходит исключение, и соединение «виснет». Это утечка ресурсов, и на продакшене такое аукнется пулом занятых соединений. Контекстный менеджер (через
with) гарантирует, что ресурс будет освобожден даже при ошибке. Это как автоматический «уборщик».2. Как это выглядит в коде?
Самый простой способ — использовать готовый менеджер из библиотеки:
import sqlite3
# Плохо: открыли, забыли закрыть
conn = sqlite3.connect('db.sqlite')
cursor = conn.cursor()
cursor.execute('SELECT 1')
# conn.close() # а если забыл?
# Хорошо: with сам закроет
with sqlite3.connect('db.sqlite') as conn:
cursor = conn.cursor()
cursor.execute('SELECT 1')
# conn закроется автоматически, даже если была ошибка
3. А если библиотека не поддерживает
with?Тогда ты пишешь свой контекстный менеджер. Это два магических метода:
class DBConnection:
def __init__(self, db_name):
self.db_name = db_name
def __enter__(self):
print('Открываю соединение')
self.conn = sqlite3.connect(self.db_name)
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
print('Закрываю соединение')
self.conn.close()
# Если вернуть False (или None), исключение пробросится дальше
# Если вернуть True — исключение «съедается»
return False
with DBConnection('db.sqlite') as conn:
cursor = conn.cursor()
cursor.execute('SELECT 1')
4. Что важно для Senior?
Ты должен понимать:
• Транзакции: внутри
with можно управлять commit/rollback. Например, если в __exit__ проверить exc_type, можно откатить транзакцию при ошибке.• Пул соединений: контекстный менеджер не обязан каждый раз создавать новое соединение. Он может брать готовое из пула (например,
psycopg2.pool) и возвращать его обратно.• Вложенные менеджеры: один
with внутри другого — норм. Но следи за порядком: сначала открываем внешний ресурс, потом внутренний.• Исключения: в
__exit__ ты получаешь тип, значение и traceback. Если вернешь True, исключение будет подавлено — делай это осознанно, только если ты его обработал.5. Пример для продакшена (на
psycopg2):from contextlib import contextmanager
import psycopg2
@contextmanager
def get_db_connection(dsn):
conn = psycopg2.connect(dsn)
try:
yield conn
conn.commit() # если всё ок
except Exception:
conn.rollback() # если ошибка
raise
finally:
conn.close()
6. Главный совет для собеса:
Не просто расскажи про
with. Скажи: «Контекстный менеджер — это паттерн для управления ресурсами. Для БД он гарантирует закрытие соединения и корректную обработку транзакций. Я использую его везде, где есть внешние ресурсы: файлы, сокеты, БД». И добавь про contextlib.contextmanager — это упрощает написание.Теперь ты готов. Иди и закрой эту тему 🔥
#python #senior #собеседование
🚀 WebSockets в FastAPI: Реальное время для твоего чата!
Представь, что ты звонишь другу: один раз набрал номер, и вы общаетесь без перерыва. WebSocket — это как такой звонок, только между браузером и сервером. Никаких постоянных проверок «а есть ли новое письмо?». Всё мгновенно!
❓ Что такое WebSocket?
Это протокол, который создаёт постоянный канал связи. Клиент (твой браузер или приложение) и сервер могут обмениваться данными в реальном времени. Инициатором может быть кто угодно — не только клиент, но и сервер. Круто, правда?
🆚 Сравним с классическим HTTP:
• HTTP: как почта. Ты отправляешь запрос, сервер отвечает. Чтобы получить новые сообщения, клиент должен постоянно «заглядывать в почтовый ящик» (делать новые запросы). Это медленно, нагружает сервер, тратит трафик.
• WebSocket: как телефонный разговор. Один раз установили соединение — и общаетесь без задержек. Сервер сам может отправить данные, когда они появляются.
🔧 Как это работает в FastAPI?
FastAPI поддерживает WebSocket «из коробки». Нужно просто создать endpoint с
Пример кода (очень простой):
Что здесь происходит?
1. Клиент подключается к
2. Сервер принимает соединение (
3. В бесконечном цикле сервер ждёт сообщение от клиента и отправляет его обратно (эхо).
💡 Для чата с комнатами нужно чуть больше логики: хранить подключения, рассылать сообщения всем участникам комнаты. Но основа та же.
🔥 Почему это важно для Senior?
На собеседовании спросят:
• Как управлять подключениями? (словарь с websocket-ами)
• Как обрабатывать отключения? (try/except, закрытие соединения)
• Как масштабировать? (через Redis Pub/Sub, чтобы несколько серверов знали друг о друге)
📌 Итог: WebSocket — must-have для real-time фич. FastAPI делает работу с ним простой и элегантной. Учись, и твой чат будет летать!
#Python #FastAPI #WebSocket
Представь, что ты звонишь другу: один раз набрал номер, и вы общаетесь без перерыва. WebSocket — это как такой звонок, только между браузером и сервером. Никаких постоянных проверок «а есть ли новое письмо?». Всё мгновенно!
❓ Что такое WebSocket?
Это протокол, который создаёт постоянный канал связи. Клиент (твой браузер или приложение) и сервер могут обмениваться данными в реальном времени. Инициатором может быть кто угодно — не только клиент, но и сервер. Круто, правда?
🆚 Сравним с классическим HTTP:
• HTTP: как почта. Ты отправляешь запрос, сервер отвечает. Чтобы получить новые сообщения, клиент должен постоянно «заглядывать в почтовый ящик» (делать новые запросы). Это медленно, нагружает сервер, тратит трафик.
• WebSocket: как телефонный разговор. Один раз установили соединение — и общаетесь без задержек. Сервер сам может отправить данные, когда они появляются.
🔧 Как это работает в FastAPI?
FastAPI поддерживает WebSocket «из коробки». Нужно просто создать endpoint с
websocket.Пример кода (очень простой):
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Эхо: {data}")
Что здесь происходит?
1. Клиент подключается к
/ws.2. Сервер принимает соединение (
accept()).3. В бесконечном цикле сервер ждёт сообщение от клиента и отправляет его обратно (эхо).
💡 Для чата с комнатами нужно чуть больше логики: хранить подключения, рассылать сообщения всем участникам комнаты. Но основа та же.
🔥 Почему это важно для Senior?
На собеседовании спросят:
• Как управлять подключениями? (словарь с websocket-ами)
• Как обрабатывать отключения? (try/except, закрытие соединения)
• Как масштабировать? (через Redis Pub/Sub, чтобы несколько серверов знали друг о друге)
📌 Итог: WebSocket — must-have для real-time фич. FastAPI делает работу с ним простой и элегантной. Учись, и твой чат будет летать!
#Python #FastAPI #WebSocket
🔥 Когда учишь Python, говорят: «это язык с динамической типизацией». В переменную x можно записать сначала число 5, потом строку "привет" — и всё заработает. Но на уровне Senior такая свобода часто приводит к ошибкам. Для борьбы с этим придумали аннотации типов.
Что такое аннотация типа?
Это как наклейка «ХРУПКОЕ» на коробке: заранее знаешь, что внутри. Аннотация не мешает программе работать, но подсказывает, что ожидается в переменной или функции.
Без подсказок:
def add(a, b):
return a + b
add(2, 3) — ок, add("кошка", "собака") — получится "кошкасобака". Ошибка вылезет только во время работы.
С аннотациями:
def add(a: int, b: int) -> int:
return a + b
После имени — двоеточие и тип, стрелка -> показывает тип возврата.
Важно: Python всё равно не проверяет типы при запуске. Это делает mypy — он анализирует код без запуска и заранее предупреждает о несоответствиях, как проверка орфографии для кода.
Union Types — «или то, или другое»
Возраст — то ли число 25, то ли строка "25". С Python 3.10:
age: int | str = 25
Mypy понимает: сюда можно int или str, но не список.
Списки с чётким содержимым
def sum_numbers(numbers: list[int | float]) -> float:
return sum(numbers)
Список, где каждый элемент int или float. Подсунешь строки — mypy предупредит заранее.
Словари с известной структурой (TypedDict)
Обычный словарь легко перепутать ключами в большом проекте. TypedDict описывает структуру чётко:
from typing import TypedDict, NotRequired
class User(TypedDict):
id: int
name: str
email: NotRequired[str]
def print_user(user: User) -> None:
print(user["name"])
IDE подскажет ключи словаря, а mypy предупредит при обращении к несуществующему полю.
Почему Senior любят аннотации типов
— Документация, которая не устаревает.
— Защита от глупых ошибок: mypy ругнётся ещё до боевого сервера.
— Быстрый рефакторинг: IDE точно знает, где какие типы меняются.
Что сказать на собеседовании
Не пересказывай синтаксис — объясни пользу: «Данные из API могут прийти как число или строка, поэтому используем int | str. А конфиги описываем через TypedDict — новый разработчик сразу видит структуру».
Итог
1. Добавляй типы в новые функции.
2. Поставь mypy (pip install mypy), запускай mypy файл.py.
3. Освоишься — пробуй list[int] и TypedDict.
Ошибки находятся на этапе написания, код становится понятнее.
Удачи! 😎
#Python #Типизация #Senior #Новичкам
Что такое аннотация типа?
Это как наклейка «ХРУПКОЕ» на коробке: заранее знаешь, что внутри. Аннотация не мешает программе работать, но подсказывает, что ожидается в переменной или функции.
Без подсказок:
def add(a, b):
return a + b
add(2, 3) — ок, add("кошка", "собака") — получится "кошкасобака". Ошибка вылезет только во время работы.
С аннотациями:
def add(a: int, b: int) -> int:
return a + b
После имени — двоеточие и тип, стрелка -> показывает тип возврата.
Важно: Python всё равно не проверяет типы при запуске. Это делает mypy — он анализирует код без запуска и заранее предупреждает о несоответствиях, как проверка орфографии для кода.
Union Types — «или то, или другое»
Возраст — то ли число 25, то ли строка "25". С Python 3.10:
age: int | str = 25
Mypy понимает: сюда можно int или str, но не список.
Списки с чётким содержимым
def sum_numbers(numbers: list[int | float]) -> float:
return sum(numbers)
Список, где каждый элемент int или float. Подсунешь строки — mypy предупредит заранее.
Словари с известной структурой (TypedDict)
Обычный словарь легко перепутать ключами в большом проекте. TypedDict описывает структуру чётко:
from typing import TypedDict, NotRequired
class User(TypedDict):
id: int
name: str
email: NotRequired[str]
def print_user(user: User) -> None:
print(user["name"])
IDE подскажет ключи словаря, а mypy предупредит при обращении к несуществующему полю.
Почему Senior любят аннотации типов
— Документация, которая не устаревает.
— Защита от глупых ошибок: mypy ругнётся ещё до боевого сервера.
— Быстрый рефакторинг: IDE точно знает, где какие типы меняются.
Что сказать на собеседовании
Не пересказывай синтаксис — объясни пользу: «Данные из API могут прийти как число или строка, поэтому используем int | str. А конфиги описываем через TypedDict — новый разработчик сразу видит структуру».
Итог
1. Добавляй типы в новые функции.
2. Поставь mypy (pip install mypy), запускай mypy файл.py.
3. Освоишься — пробуй list[int] и TypedDict.
Ошибки находятся на этапе написания, код становится понятнее.
Удачи! 😎
#Python #Типизация #Senior #Новичкам
🚀 ТЫ ДУМАЕШЬ, ЧТО ЗНАЕШЬ ПРО ПУЛ СОЕДИНЕНИЙ? 🚀
Сеньор-разработчик отличается от джуна не только количеством кода, но и пониманием того, как этот код работает под капотом. Сегодня разберем одну из таких тем — пул соединений (connection pool). Это то, что превращает твое приложение из «черепахи» в «гепарда».
Что такое пул соединений и зачем он нужен?
Представь, что каждое обращение к базе данных — это как зайти в кафе и каждый раз заново искать столик, делать заказ и ждать. Соединение с БД — дорогая операция (по времени и ресурсам). Пул соединений — это как забронировать несколько столиков заранее. Ты просто подходишь, садишься и сразу получаешь кофе. Экономия времени — колоссальная!
Как это выглядит в коде?
В Python, особенно при работе с PostgreSQL через библиотеку
Видишь? Вместо того чтобы каждый раз вызывать
Какие бывают типы пулов?
В
• AbstractConnectionPool — это «скелет», абстрактный класс. На его основе можно сделать свой пул.
• SimpleConnectionPool — для однопоточных приложений. Самый простой и понятный.
• ThreadedConnectionPool — для многопоточных. Пул можно смело использовать из разных потоков.
• PersistentConnectionPool — для многопоточных приложений, где каждому потоку нужно «свое» постоянное соединение. Чаще используется со старыми фреймворками вроде Zope.
Главные методы, которые нужно знать:
•
•
•
Совет от сеньора:
Не используй
Почему это важно на собеседовании?
Потому что это показывает, что ты понимаешь, как оптимизировать работу с ресурсами. Сеньор — это не просто «код писать», это «делать так, чтобы код не упал под нагрузкой». А пул соединений — один из ключевых инструментов для этого.
#Python #Senior #БазыДанных
Сеньор-разработчик отличается от джуна не только количеством кода, но и пониманием того, как этот код работает под капотом. Сегодня разберем одну из таких тем — пул соединений (connection pool). Это то, что превращает твое приложение из «черепахи» в «гепарда».
Что такое пул соединений и зачем он нужен?
Представь, что каждое обращение к базе данных — это как зайти в кафе и каждый раз заново искать столик, делать заказ и ждать. Соединение с БД — дорогая операция (по времени и ресурсам). Пул соединений — это как забронировать несколько столиков заранее. Ты просто подходишь, садишься и сразу получаешь кофе. Экономия времени — колоссальная!
Как это выглядит в коде?
В Python, особенно при работе с PostgreSQL через библиотеку
psycopg2, есть встроенные инструменты. Давай посмотрим на самый простой пример:from psycopg2 import pool
# Создаем пул: минимум 2 соединения, максимум 10
connection_pool = pool.SimpleConnectionPool(
2, 10,
user="myuser",
password="mypass",
host="localhost",
port="5432",
database="mydb"
)
# Берем соединение из пула
conn = connection_pool.getconn()
# Работаем с базой
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
# Возвращаем соединение обратно в пул
connection_pool.putconn(conn)
# Когда всё сделано — закрываем все соединения
connection_pool.closeall()
Видишь? Вместо того чтобы каждый раз вызывать
psycopg2.connect(), мы один раз создаем пул и потом просто берем готовые соединения. Это даёт огромный прирост производительности.Какие бывают типы пулов?
В
psycopg2 есть четыре класса. Не пугайся, всё просто:• AbstractConnectionPool — это «скелет», абстрактный класс. На его основе можно сделать свой пул.
• SimpleConnectionPool — для однопоточных приложений. Самый простой и понятный.
• ThreadedConnectionPool — для многопоточных. Пул можно смело использовать из разных потоков.
• PersistentConnectionPool — для многопоточных приложений, где каждому потоку нужно «свое» постоянное соединение. Чаще используется со старыми фреймворками вроде Zope.
Главные методы, которые нужно знать:
•
getconn(key=None) — взять соединение из пула. Если передать key, то вернется соединение, привязанное к этому ключу (актуально для PersistentConnectionPool).•
putconn(connection, key=None, close=False) — вернуть соединение обратно. Если close=True, то соединение удалится из пула.•
closeall() — закрыть все соединения в пуле. Обязательно вызывай при завершении работы приложения!Совет от сеньора:
Не используй
SimpleConnectionPool в веб-приложениях с многопоточностью (например, Flask или Django с WSGI). Там нужно ThreadedConnectionPool. Иначе получишь race condition и странные ошибки.Почему это важно на собеседовании?
Потому что это показывает, что ты понимаешь, как оптимизировать работу с ресурсами. Сеньор — это не просто «код писать», это «делать так, чтобы код не упал под нагрузкой». А пул соединений — один из ключевых инструментов для этого.
#Python #Senior #БазыДанных
🚀 Что такое Dependency Injection в Django и как его настроить? Разбор для Senior!
Вы на собеседовании. Вас спрашивают: "Как вы управляете зависимостями в Django? Используете ли Dependency Injection?"
Если вы ответите: "Ну, я просто создаю объекты внутри view..." — это провал. Senior должен мыслить архитектурно.
Давайте разберем, что это за зверь и как его приручить.
🔍 Суть Dependency Injection (DI) — это когда объект не создает свои зависимости сам, а получает их "снаружи".
Представьте: ваш view должен отправить email. Вместо того чтобы писать:
Вы делаете так:
Зачем это нужно?
• Тестируемость: вы легко подменяете реальный EmailSender на заглушку (mock).
• Гибкость: можно переключиться с одного сервиса на другой без изменения кода view.
• Чистота кода: view не знает, как создаются зависимости — это его не касается.
🛠 Как реализовать DI в Django?
Django из коробки не имеет встроенного DI-контейнера (как Spring в Java). Но это не проблема — мы можем сделать это сами.
Самый простой и элегантный способ — через декораторы или фабрики.
Рассмотрим подход с ServiceInjector (класс, который регистрирует зависимости и внедряет их в функции).
Пример:
Как это работает?
1. Регистрация: декоратор
2. Внедрение: декоратор
3. Внутри view вы просто берете нужную зависимость из
Где регистрировать зависимости? Лучше в отдельном модуле
🔥 Альтернатива для простых случаев — передача зависимостей через конструктор класса-контроллера (как в примере из Stack Overflow).
Важно: DI — это не серебряная пуля. В маленьких проектах он может быть избыточен. Но для больших, тестируемых систем — это must have.
На собеседовании покажите, что вы понимаете не только механику, но и зачем это нужно. Senior отличает умение выбирать правильный инструмент под задачу.
#python #django #senior
Вы на собеседовании. Вас спрашивают: "Как вы управляете зависимостями в Django? Используете ли Dependency Injection?"
Если вы ответите: "Ну, я просто создаю объекты внутри view..." — это провал. Senior должен мыслить архитектурно.
Давайте разберем, что это за зверь и как его приручить.
🔍 Суть Dependency Injection (DI) — это когда объект не создает свои зависимости сам, а получает их "снаружи".
Представьте: ваш view должен отправить email. Вместо того чтобы писать:
def my_view(request):
mailer = EmailSender() # ❌ Жесткая связь
mailer.send("Hello")Вы делаете так:
def my_view(request, mailer): # ✅ Зависимость приходит извне
mailer.send("Hello")Зачем это нужно?
• Тестируемость: вы легко подменяете реальный EmailSender на заглушку (mock).
• Гибкость: можно переключиться с одного сервиса на другой без изменения кода view.
• Чистота кода: view не знает, как создаются зависимости — это его не касается.
🛠 Как реализовать DI в Django?
Django из коробки не имеет встроенного DI-контейнера (как Spring в Java). Но это не проблема — мы можем сделать это сами.
Самый простой и элегантный способ — через декораторы или фабрики.
Рассмотрим подход с ServiceInjector (класс, который регистрирует зависимости и внедряет их в функции).
Пример:
from functools import wraps
class ServiceInjector:
def __init__(self):
self.deps = {}
def register(self, name=None):
def decorator(thing):
nonlocal name
if not name:
if not hasattr(thing, '__name__'):
raise Exception("no name")
name = thing.__name__
self.deps[name] = thing
return thing
return decorator
def inject(self, func):
@wraps(func)
def decorated(*args, **kwargs):
# Добавляем словарь зависимостей как последний аргумент
new_args = args + (self.deps,)
return func(*new_args, **kwargs)
return decorated
# Использование:
si = ServiceInjector()
@si.register()
def send_email(msg):
print(f"Sending: {msg}")
@si.register(name="PI")
PI = 3.14159
@si.inject
def my_view(request, _deps):
email_sender = _deps['send_email']
pi = _deps['PI']
email_sender("Hello from DI!")
print(pi)
Как это работает?
1. Регистрация: декоратор
@si.register() добавляет функцию или класс в словарь deps под её именем (или под кастомным именем, если указать name).2. Внедрение: декоратор
@si.inject автоматически добавляет словарь _deps последним аргументом в функцию.3. Внутри view вы просто берете нужную зависимость из
_deps.Где регистрировать зависимости? Лучше в отдельном модуле
di_config.py или прямо в urls.py.🔥 Альтернатива для простых случаев — передача зависимостей через конструктор класса-контроллера (как в примере из Stack Overflow).
class ApiController:
def __init__(self, **deps):
self.deps = deps
def get_all_posts(self, request):
svc = self.deps['post_service']
# ...
# В urls.py:
urlpatterns = [
path("posts/", ApiController(post_service=PostService()).get_all_posts),
]
Важно: DI — это не серебряная пуля. В маленьких проектах он может быть избыточен. Но для больших, тестируемых систем — это must have.
На собеседовании покажите, что вы понимаете не только механику, но и зачем это нужно. Senior отличает умение выбирать правильный инструмент под задачу.
#python #django #senior
🔥 Что такое миграции в Django и как их создавать? Разбираем вопрос для собеседования на Senior Python Developer!
Представь: ты работаешь над интернет-магазином. Внезапно нужно добавить поле «скидка» к модели товара. Ты просто дописываешь код, заливаешь на сервер… и сайт падает с ошибкой! 😱 Почему? Потому что база данных не знает о новом поле — её структура устарела.
Вот тут и приходят на помощь миграции. Это как система контроля версий для твоей базы данных. Они хранят инструкции по изменению схемы БД (добавить колонку, создать таблицу и т.д.) и позволяют применять эти изменения без потери данных. 🚀
Как это работает?
1. Ты меняешь модели в
2. Запускаешь команду:
Django сравнивает текущие модели с последним сохранённым состоянием и создаёт файл миграции (в папке
3. Применяешь миграцию:
Django выполняет инструкции из файла и обновляет базу данных. Все применённые миграции записываются в специальную таблицу
Важные детали для Senior-уровня:
• Каждая миграция содержит два метода:
• Django строит граф зависимостей между миграциями разных приложений, чтобы применять их в правильном порядке. 🧩
• Команда
•
•
•
Пример из жизни:
Вместо того чтобы сразу выкатывать новый код, опытный разработчик сначала создаёт миграцию, применяет её (отдельным деплоем), и только потом заливает код, который использует новое поле. Сайт работает без простоев! 👌
Главный совет:
Миграции — это твой друг. Не бойся их, а используй осознанно. На собеседовании покажи, что понимаешь не только команды, но и внутреннее устройство: таблицу
Прокачайся и стань тем Senior, который не ломает прод! 💪
#Django #Python #Senior
Представь: ты работаешь над интернет-магазином. Внезапно нужно добавить поле «скидка» к модели товара. Ты просто дописываешь код, заливаешь на сервер… и сайт падает с ошибкой! 😱 Почему? Потому что база данных не знает о новом поле — её структура устарела.
Вот тут и приходят на помощь миграции. Это как система контроля версий для твоей базы данных. Они хранят инструкции по изменению схемы БД (добавить колонку, создать таблицу и т.д.) и позволяют применять эти изменения без потери данных. 🚀
Как это работает?
1. Ты меняешь модели в
models.py.2. Запускаешь команду:
python manage.py makemigrations
Django сравнивает текущие модели с последним сохранённым состоянием и создаёт файл миграции (в папке
migrations/ твоего приложения). Это обычный Python-файл с инструкциями: «добавить поле», «создать индекс» и т.п.3. Применяешь миграцию:
python manage.py migrate
Django выполняет инструкции из файла и обновляет базу данных. Все применённые миграции записываются в специальную таблицу
django_migrations — так система знает, что уже сделано.Важные детали для Senior-уровня:
• Каждая миграция содержит два метода:
forwards() (как применить) и backwards() (как откатить).• Django строит граф зависимостей между миграциями разных приложений, чтобы применять их в правильном порядке. 🧩
• Команда
makemigrations имеет полезные опции:•
--name NAME — задать имя миграции (например, add_discount_field).•
--empty — создать пустую миграцию для ручного написания сложных операций.•
--dry-run — показать, что будет создано, но не создавать файлы.Пример из жизни:
Вместо того чтобы сразу выкатывать новый код, опытный разработчик сначала создаёт миграцию, применяет её (отдельным деплоем), и только потом заливает код, который использует новое поле. Сайт работает без простоев! 👌
Главный совет:
Миграции — это твой друг. Не бойся их, а используй осознанно. На собеседовании покажи, что понимаешь не только команды, но и внутреннее устройство: таблицу
django_migrations, граф зависимостей, ручное редактирование миграций.Прокачайся и стань тем Senior, который не ломает прод! 💪
#Django #Python #Senior
🚀 **КАК РЕАЛИЗОВАТЬ АСИНХРОННЫЕ ЗАДАЧИ С ПОМОЩЬЮ CELERY?**
Представь: пользователь регистрируется, загружает фото и ждет... и ждет... пока сервер обработает изображение и отправит письмо. 🙈 Это путь в никуда. На собеседовании Senior тебя спросят: «Как вынести тяжелые задачи из основного потока?» — и тут твой звездный час!
**Что такое Celery?**
Это библиотека для создания очередей задач. Она берет на себя «тяжелую работу» (отправка писем, обработка фото, ML-модели) и выполняет ее в фоне, не блокируя ответ пользователю.
**Как это работает (простыми словами):**
1. Пользователь отправляет запрос (например, POST на загрузку фото).
2. FastAPI (или Flask) не обрабатывает фото сам, а кладет задачу в очередь (Redis или RabbitMQ).
3. Celery Worker (отдельный процесс) забирает задачу из очереди и выполняет ее.
4. Пользователь сразу получает ответ: «Задача принята, ID: 123». И может спокойно заниматься своими делами.
5. Клиент (фронтенд) может периодически спрашивать сервер: «Ну что, задача готова?» (это называется polling).
**Когда использовать Celery, а не
-
- Celery — нужен для:
• CPU-интенсивных задач (обработка видео, генерация отчетов).
• Задач, которые должны выполняться гарантированно (с повторными попытками).
• Когда нужно отслеживать статус задачи (выполнена/ошибка).
• Когда задач много и нужна очередь с приоритетами.
**Пример кода (очень простой):**
**Как это запустить?**
1. Установить Redis (брокер сообщений) — можно через Docker.
2. Установить Celery:
3. Запустить воркер:
4. Запустить FastAPI приложение.
**Почему это важно для Senior?**
Senior должен понимать архитектуру: как отделить синхронный код от асинхронного, как масштабировать воркеры, как мониторить задачи (через Flower), как обрабатывать ошибки и повторные попытки. Celery — это стандарт индустрии.
**Коротко: что сказать на собеседовании?**
- Celery + Redis — это очередь задач.
- Используем для тяжелых фоновых процессов.
- Worker — отдельный процесс, который выполняет задачи.
- Клиент получает ID задачи и может опрашивать статус.
Готовься, это один из ключевых вопросов! 💪
#Python #Celery #Senior
Представь: пользователь регистрируется, загружает фото и ждет... и ждет... пока сервер обработает изображение и отправит письмо. 🙈 Это путь в никуда. На собеседовании Senior тебя спросят: «Как вынести тяжелые задачи из основного потока?» — и тут твой звездный час!
**Что такое Celery?**
Это библиотека для создания очередей задач. Она берет на себя «тяжелую работу» (отправка писем, обработка фото, ML-модели) и выполняет ее в фоне, не блокируя ответ пользователю.
**Как это работает (простыми словами):**
1. Пользователь отправляет запрос (например, POST на загрузку фото).
2. FastAPI (или Flask) не обрабатывает фото сам, а кладет задачу в очередь (Redis или RabbitMQ).
3. Celery Worker (отдельный процесс) забирает задачу из очереди и выполняет ее.
4. Пользователь сразу получает ответ: «Задача принята, ID: 123». И может спокойно заниматься своими делами.
5. Клиент (фронтенд) может периодически спрашивать сервер: «Ну что, задача готова?» (это называется polling).
**Когда использовать Celery, а не
BackgroundTasks из FastAPI?**-
BackgroundTasks — легковесный, работает в том же процессе. Подходит для быстрых задач (отправить уведомление).- Celery — нужен для:
• CPU-интенсивных задач (обработка видео, генерация отчетов).
• Задач, которые должны выполняться гарантированно (с повторными попытками).
• Когда нужно отслеживать статус задачи (выполнена/ошибка).
• Когда задач много и нужна очередь с приоритетами.
**Пример кода (очень простой):**
# tasks.py
from celery import Celery
app = Celery('tasks', broker='redis://localhost:6379/0')
@app.task
def send_email(email, message):
# тут логика отправки
print(f"Отправляем {message} на {email}")
return "OK"
# main.py (FastAPI)
from fastapi import FastAPI
from tasks import send_email
app = FastAPI()
@app.post("/send")
def send_email_endpoint(email: str):
task = send_email.delay(email, "Привет!")
return {"task_id": task.id, "status": "Queued"}
**Как это запустить?**
1. Установить Redis (брокер сообщений) — можно через Docker.
2. Установить Celery:
pip install celery[redis].3. Запустить воркер:
celery -A tasks worker --loglevel=info.4. Запустить FastAPI приложение.
**Почему это важно для Senior?**
Senior должен понимать архитектуру: как отделить синхронный код от асинхронного, как масштабировать воркеры, как мониторить задачи (через Flower), как обрабатывать ошибки и повторные попытки. Celery — это стандарт индустрии.
**Коротко: что сказать на собеседовании?**
- Celery + Redis — это очередь задач.
- Используем для тяжелых фоновых процессов.
- Worker — отдельный процесс, который выполняет задачи.
- Клиент получает ID задачи и может опрашивать статус.
Готовься, это один из ключевых вопросов! 💪
#Python #Celery #Senior
🔥 СЛОЖНАЯ ТЕМА, КОТОРУЮ СПРАШИВАЮТ НА КАЖДОМ СОБЕСЕДОВАНИИ: SAGA PATTERN В PYTHON. РАЗБИРАЕМСЯ, ПОКА ГОРИТО! 🔥
Представь: ты — архитектор огромного интернет-магазина. У тебя есть отдельные сервисы: заказы, платежи, склад, доставка. И вот клиент оформляет заказ. Ты создаёшь запись в БД заказов, списываешь деньги, резервируешь товар, отправляешь доставку. Всё бы ничего, но КАЖДЫЙ СЕРВИС ХРАНИТ ДАННЫЕ В СВОЕЙ БАЗЕ. И тут склад падает — товара нет. Деньги списаны, а заказ не выполнен. КАК ОТКАТИТЬ ПЛАТЁЖ? 🤯
В монолите ты бы просто сделал ROLLBACK, и все изменения отменились бы. Но в микросервисах ACID-транзакции между разными БД НЕ РАБОТАЮТ. И тут на сцену выходит SAGA PATTERN — твой спаситель! 🦸♂️
ЧТО ТАКОЕ SAGA?
Это паттерн, который разбивает большую распределённую транзакцию на последовательность маленьких локальных транзакций. Каждый шаг — это отдельная операция в одном сервисе. Если что-то пошло не так, запускаются КОМПЕНСИРУЮЩИЕ ТРАНЗАКЦИИ — они отменяют уже сделанные шаги и возвращают систему в консистентное состояние.
ЕСТЬ ДВА ПОДХОДА:
1️⃣ CHOREOGRAPHY (ХОРЕОГРАФИЯ) — децентрализованный танец без дирижёра. Каждый сервис слушает события (например, через Kafka) и сам решает, что делать дальше. Создал заказ → опубликовал событие OrderCreated → платёжный сервис услышал и списал деньги → опубликовал PaymentReserved → склад услышал и зарезервировал товар... И так по цепочке. Если шаг упал, сервис публикует событие об ошибке, и предыдущие сервисы делают компенсацию.
Плюсы: сервисы полностью независимы, нет единой точки отказа.
Минусы: при длинных цепочках сложно понять, что вообще происходит. А если сервисы зациклились — пиши пропало! 😅
2️⃣ ORCHESTRATION (ОРКЕСТРОВКА) — есть центральный координатор (оркестратор), который управляет всеми шагами. Он говорит: «Платеж, спиши деньги!», «Склад, зарезервируй товар!». Если что-то падает, оркестратор сам запускает компенсации.
Плюсы: весь процесс виден в одном месте, легко отлаживать.
Минусы: оркестратор — это потенциальное узкое место и единая точка отказа.
КАК РЕАЛИЗОВАТЬ SAGA В PYTHON?
Для хореографии — используй Kafka или RabbitMQ. Каждый сервис — отдельное приложение на FastAPI или Django, которое слушает события. Примерно так:
Для оркестрации — можно использовать библиотеку
ВАЖНЫЙ НЮАНС, О КОТОРОМ ЧАСТО ВРУТ:
Некоторые говорят, что Saga — это просто «транзакция с откатом». НЕТ! Компенсации — это НЕ откат. Они выполняются уже ПОСЛЕ того, как локальная транзакция закоммичена. И они должны быть идемпотентными — если компенсация выполнится дважды, не должно быть ошибки.
КОГДА ЧТО ВЫБРАТЬ?
- Хореографию — для простых цепочек (2-4 шага) и когда важна независимость сервисов.
- Оркестрацию — для сложных процессов с ветвлениями и когда нужен чёткий контроль.
А теперь вопрос к тебе: какой подход ты бы выбрал для своего проекта и почему? Пиши в комментариях! 👇
#python #saga #микросервисы #собеседование #архитектура
Представь: ты — архитектор огромного интернет-магазина. У тебя есть отдельные сервисы: заказы, платежи, склад, доставка. И вот клиент оформляет заказ. Ты создаёшь запись в БД заказов, списываешь деньги, резервируешь товар, отправляешь доставку. Всё бы ничего, но КАЖДЫЙ СЕРВИС ХРАНИТ ДАННЫЕ В СВОЕЙ БАЗЕ. И тут склад падает — товара нет. Деньги списаны, а заказ не выполнен. КАК ОТКАТИТЬ ПЛАТЁЖ? 🤯
В монолите ты бы просто сделал ROLLBACK, и все изменения отменились бы. Но в микросервисах ACID-транзакции между разными БД НЕ РАБОТАЮТ. И тут на сцену выходит SAGA PATTERN — твой спаситель! 🦸♂️
ЧТО ТАКОЕ SAGA?
Это паттерн, который разбивает большую распределённую транзакцию на последовательность маленьких локальных транзакций. Каждый шаг — это отдельная операция в одном сервисе. Если что-то пошло не так, запускаются КОМПЕНСИРУЮЩИЕ ТРАНЗАКЦИИ — они отменяют уже сделанные шаги и возвращают систему в консистентное состояние.
ЕСТЬ ДВА ПОДХОДА:
1️⃣ CHOREOGRAPHY (ХОРЕОГРАФИЯ) — децентрализованный танец без дирижёра. Каждый сервис слушает события (например, через Kafka) и сам решает, что делать дальше. Создал заказ → опубликовал событие OrderCreated → платёжный сервис услышал и списал деньги → опубликовал PaymentReserved → склад услышал и зарезервировал товар... И так по цепочке. Если шаг упал, сервис публикует событие об ошибке, и предыдущие сервисы делают компенсацию.
Плюсы: сервисы полностью независимы, нет единой точки отказа.
Минусы: при длинных цепочках сложно понять, что вообще происходит. А если сервисы зациклились — пиши пропало! 😅
2️⃣ ORCHESTRATION (ОРКЕСТРОВКА) — есть центральный координатор (оркестратор), который управляет всеми шагами. Он говорит: «Платеж, спиши деньги!», «Склад, зарезервируй товар!». Если что-то падает, оркестратор сам запускает компенсации.
Плюсы: весь процесс виден в одном месте, легко отлаживать.
Минусы: оркестратор — это потенциальное узкое место и единая точка отказа.
КАК РЕАЛИЗОВАТЬ SAGA В PYTHON?
Для хореографии — используй Kafka или RabbitMQ. Каждый сервис — отдельное приложение на FastAPI или Django, которое слушает события. Примерно так:
# Сервис заказов (публикует событие)
from kafka import KafkaProducer
producer = KafkaProducer(bootstrap_servers='localhost:9092')
producer.send('orders', key=b'order-123', value=b'OrderCreated')
# Сервис платежей (слушает событие)
from kafka import KafkaConsumer
consumer = KafkaConsumer('orders', bootstrap_servers='localhost:9092')
for msg in consumer:
if msg.value == b'OrderCreated':
# списываем деньги
# публикуем PaymentReserved или PaymentFailed
Для оркестрации — можно использовать библиотеку
saga-pattern или написать свой координатор на asyncio. Например:async def create_order_saga():
try:
await create_order()
await reserve_payment()
await reserve_inventory()
await create_shipping()
except Exception:
await cancel_payment()
await cancel_order()
ВАЖНЫЙ НЮАНС, О КОТОРОМ ЧАСТО ВРУТ:
Некоторые говорят, что Saga — это просто «транзакция с откатом». НЕТ! Компенсации — это НЕ откат. Они выполняются уже ПОСЛЕ того, как локальная транзакция закоммичена. И они должны быть идемпотентными — если компенсация выполнится дважды, не должно быть ошибки.
КОГДА ЧТО ВЫБРАТЬ?
- Хореографию — для простых цепочек (2-4 шага) и когда важна независимость сервисов.
- Оркестрацию — для сложных процессов с ветвлениями и когда нужен чёткий контроль.
А теперь вопрос к тебе: какой подход ты бы выбрал для своего проекта и почему? Пиши в комментариях! 👇
#python #saga #микросервисы #собеседование #архитектура
ТВОЙ asyncio-КОД МОЖЕТ ТИХО УПАСТЬ НА ПРОДЕ, И ТЫ ДАЖЕ НЕ ПОЙМЁШЬ ПОЧЕМУ!
Сколько раз ты писал что-то вроде «async with lock:» и надеялся, что всё будет работать? А потом — бац! — и данные в кэше дублируются, или 1000 запросов летит на сервер, хотя нужно было всего 5. Знакомо? Тогда этот разбор — для тебя.
ДАВАЙ РАЗБЕРЁМСЯ, КАК РЕАЛЬНО РАБОТАЮТ БЛОКИРОВКИ В ASYNCIO.
1️⃣ ПРОБЛЕМА: ПОЧЕМУ ОНИ ВООБЩЕ НУЖНЫ?
asyncio — однопоточный. Казалось бы, откуда взяться гонкам? Но вот в чём фишка: задачи выполняются ПООЧЕРЁДНО, переключаясь в момент await. Представь: две задачи одновременно проверяют кэш. Обе видят, что данных нет. Обе делают await на запрос к сайту. И вот уже ДВА запроса улетели вместо одного!
Это классическая гонка (race condition). Она возникает, когда между проверкой условия и изменением состояния есть точка переключения контекста.
2️⃣ РЕШЕНИЕ: asyncio.Lock
Lock — это как «туалет с замком»: один зашёл, закрылся, другие ждут снаружи. Пока первый не выйдет и не откроет, никто не войдёт.
Вот как это выглядит в коде:
⚠️ КРИТИЧЕСКИЙ МОМЕНТ: блокировка должна захватываться ДО проверки условия! Если ты поставишь async with lock только вокруг самого запроса, гонка останется. Запомни: защищаем ВЕСЬ критический участок, от проверки до обновления.
3️⃣ НО ЕСТЬ НЮАНС: Lock НЕ ограничивает количество
Lock пропускает только ОДНУ задачу. А что если нам нужно разрешить, скажем, 5 одновременных подключений к базе? Тут на сцену выходит Semaphore.
4️⃣ asyncio.Semaphore: ОГРАНИЧИТЕЛЬ ПОТОКА
Semaphore — это как «шлагбаум на парковке»: внутри счётчик. Каждый async with уменьшает счётчик на 1, выход — увеличивает. Когда счётчик = 0, все ждут.
ВОТ ТАК МЫ ОГРАНИЧИВАЕМ НАГРУЗКУ НА ВНЕШНИЙ СЕРВИС И НЕ ЛОВИМ 429-ю ошибку!
5️⃣ ЧАСТЫЕ ОШИБКИ, КОТОРЫЕ ВАЛЯТ НА СОБЕСЕДОВАНИИ
❌ Захват блокировки ВНУТРИ проверки — гонка остаётся.
❌ Забыл освободить Lock (не через async with) — дедлок. Все задачи вечно ждут.
❌ Использование Lock вместо Semaphore, когда нужно N одновременных.
❌ Создание нового Lock на каждую задачу — блокировка не работает вообще.
6️⃣ КАК ОТВЕЧАТЬ НА СОБЕСЕДОВАНИИ?
Расскажи так:
«В asyncio гонки возникают из-за переключения контекста в точках await. Lock гарантирует эксклюзивный доступ к ресурсу, а Semaphore — ограничение количества одновременных доступов. Главное — защищать весь критический участок, а не только его часть».
И добавь пример с кэшем — это покажет, что ты понимаешь, а не зазубрил.
А ТЫ УЖЕ ЛОВИЛ ТАКИЕ БАГИ? ДЕЛИСЬ В КОММЕНТАРИЯХ! 👇
#python #asyncio #собеседование #блокировки #senior
Сколько раз ты писал что-то вроде «async with lock:» и надеялся, что всё будет работать? А потом — бац! — и данные в кэше дублируются, или 1000 запросов летит на сервер, хотя нужно было всего 5. Знакомо? Тогда этот разбор — для тебя.
ДАВАЙ РАЗБЕРЁМСЯ, КАК РЕАЛЬНО РАБОТАЮТ БЛОКИРОВКИ В ASYNCIO.
1️⃣ ПРОБЛЕМА: ПОЧЕМУ ОНИ ВООБЩЕ НУЖНЫ?
asyncio — однопоточный. Казалось бы, откуда взяться гонкам? Но вот в чём фишка: задачи выполняются ПООЧЕРЁДНО, переключаясь в момент await. Представь: две задачи одновременно проверяют кэш. Обе видят, что данных нет. Обе делают await на запрос к сайту. И вот уже ДВА запроса улетели вместо одного!
Это классическая гонка (race condition). Она возникает, когда между проверкой условия и изменением состояния есть точка переключения контекста.
2️⃣ РЕШЕНИЕ: asyncio.Lock
Lock — это как «туалет с замком»: один зашёл, закрылся, другие ждут снаружи. Пока первый не выйдет и не откроет, никто не войдёт.
Вот как это выглядит в коде:
lock = asyncio.Lock()
async def get_value(key):
async with lock: # ждём, пока освободится
if key not in cache:
value = await request_remote()
cache[key] = value
else:
value = cache[key]
return value
⚠️ КРИТИЧЕСКИЙ МОМЕНТ: блокировка должна захватываться ДО проверки условия! Если ты поставишь async with lock только вокруг самого запроса, гонка останется. Запомни: защищаем ВЕСЬ критический участок, от проверки до обновления.
3️⃣ НО ЕСТЬ НЮАНС: Lock НЕ ограничивает количество
Lock пропускает только ОДНУ задачу. А что если нам нужно разрешить, скажем, 5 одновременных подключений к базе? Тут на сцену выходит Semaphore.
4️⃣ asyncio.Semaphore: ОГРАНИЧИТЕЛЬ ПОТОКА
Semaphore — это как «шлагбаум на парковке»: внутри счётчик. Каждый async with уменьшает счётчик на 1, выход — увеличивает. Когда счётчик = 0, все ждут.
sem = asyncio.Semaphore(5) # максимум 5 одновременных
async def fetch(url):
async with sem:
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.text()
ВОТ ТАК МЫ ОГРАНИЧИВАЕМ НАГРУЗКУ НА ВНЕШНИЙ СЕРВИС И НЕ ЛОВИМ 429-ю ошибку!
5️⃣ ЧАСТЫЕ ОШИБКИ, КОТОРЫЕ ВАЛЯТ НА СОБЕСЕДОВАНИИ
❌ Захват блокировки ВНУТРИ проверки — гонка остаётся.
❌ Забыл освободить Lock (не через async with) — дедлок. Все задачи вечно ждут.
❌ Использование Lock вместо Semaphore, когда нужно N одновременных.
❌ Создание нового Lock на каждую задачу — блокировка не работает вообще.
6️⃣ КАК ОТВЕЧАТЬ НА СОБЕСЕДОВАНИИ?
Расскажи так:
«В asyncio гонки возникают из-за переключения контекста в точках await. Lock гарантирует эксклюзивный доступ к ресурсу, а Semaphore — ограничение количества одновременных доступов. Главное — защищать весь критический участок, а не только его часть».
И добавь пример с кэшем — это покажет, что ты понимаешь, а не зазубрил.
А ТЫ УЖЕ ЛОВИЛ ТАКИЕ БАГИ? ДЕЛИСЬ В КОММЕНТАРИЯХ! 👇
#python #asyncio #собеседование #блокировки #senior
ТЫ ДУМАЕШЬ, ЧТО ЗНАЕШЬ SOLID? А СЛАБО ОБЪЯСНИТЬ, ПОЧЕМУ ТВОЙ DJANGO-ПРОЕКТ РАЗВАЛИВАЕТСЯ, КОГДА ТЫ МЕНЯЕШЬ ОДНУ СТРОЧКУ В МОДЕЛИ? 😱
Скорее всего, ты нарушаешь Dependency Inversion Principle (DIP) — принцип инверсии зависимостей. И сегодня мы разберем его так, что на собеседовании ты будешь сыпать терминами и примерами, как сеньор с 10-летним стажем. Поехали!
СУТЬ ПРИНЦИПА ОДНОЙ ФРАЗОЙ
Это правило говорит: НЕ ЗАВИСИ ОТ КОНКРЕТИКИ, ЗАВИСИ ОТ АБСТРАКЦИИ. Твой высокоуровневый код (бизнес-логика) не должен знать, как именно работает низкоуровневый код (база данных, API). Они оба должны общаться через интерфейс (абстракцию).
ПОЧЕМУ ЭТО ВАЖНО?
Представь, что твой сервис отправки писем — это розетка. А конкретная реализация (SMTP, Slack, Telegram) — это вилка. Если ты в коде жёстко прописал «шлём через SMTP», то при смене провайдера тебе придётся переписывать всю бизнес-логику. А если ты воткнул переходник (интерфейс), то просто меняешь вилку — и всё работает. 🔌
КАК ЭТО ВЫГЛЯДИТ В DJANGO?
Самый частый анти-паттерн — когда ты в
А теперь — правильный подход. Мы создаём абстракцию (интерфейс) для работы с заказами:
Теперь твоя бизнес-логика зависит от интерфейса
КАК ПРИМЕНИТЬ ЭТО В РЕАЛЬНОМ DJANGO-ПРОЕКТЕ?
1. Выдели интерфейсы для работы с данными. Не таскай
2. Используй внедрение зависимостей (DI). Передавай зависимости в конструктор или метод, а не создавай их внутри. В Django для этого часто используют
3. Для сложной бизнес-логики — отдельный слой. Не пиши всё во
ЧАСТАЯ ОШИБКА НА СОБЕСЕДОВАНИИ
Многие путают DIP с Dependency Injection (DI). Запомни: DIP — это принцип (ЧТО делать), а DI — это паттерн (КАК делать). DIP говорит «зависи от абстракций», а DI предлагает способ это реализовать — передавать зависимости извне.
ПОЧЕМУ ЭТО СПАСЁТ ТВОЙ ПРОЕКТ?
• Тестируемость. Ты можешь подменить реальный репозиторий на фейковый (mock) в тестах, не трогая базу данных.
• Гибкость. Смена технологий становится простой заменой одной реализации на другую.
• Читаемость. Код становится чище, потому что бизнес-логика не засорена деталями работы с БД.
ИТОГ
DIP — это не просто абстрактная теория из книжек. Это реальный инструмент, который делает твой код на Django гибким, тестируемым и готовым к изменениям. Начни с малого — вынеси работу с моделями в репозитории, и ты почувствуешь разницу.
А теперь вопрос к тебе: сколько мест в твоём проекте напрямую обращаются к
#Python #Django #SOLID #DIP #Собеседование #SeniorPython
Скорее всего, ты нарушаешь Dependency Inversion Principle (DIP) — принцип инверсии зависимостей. И сегодня мы разберем его так, что на собеседовании ты будешь сыпать терминами и примерами, как сеньор с 10-летним стажем. Поехали!
СУТЬ ПРИНЦИПА ОДНОЙ ФРАЗОЙ
Это правило говорит: НЕ ЗАВИСИ ОТ КОНКРЕТИКИ, ЗАВИСИ ОТ АБСТРАКЦИИ. Твой высокоуровневый код (бизнес-логика) не должен знать, как именно работает низкоуровневый код (база данных, API). Они оба должны общаться через интерфейс (абстракцию).
ПОЧЕМУ ЭТО ВАЖНО?
Представь, что твой сервис отправки писем — это розетка. А конкретная реализация (SMTP, Slack, Telegram) — это вилка. Если ты в коде жёстко прописал «шлём через SMTP», то при смене провайдера тебе придётся переписывать всю бизнес-логику. А если ты воткнул переходник (интерфейс), то просто меняешь вилку — и всё работает. 🔌
КАК ЭТО ВЫГЛЯДИТ В DJANGO?
Самый частый анти-паттерн — когда ты в
views.py или services.py напрямую дёргаешь модель и её методы. Вот так делать НЕЛЬЗЯ:
# ❌ ПЛОХО: жёсткая зависимость от модели
from .models import Order
def send_invoice(order_id):
order = Order.objects.get(id=order_id)
# ... логика, которая знает про поля модели
А теперь — правильный подход. Мы создаём абстракцию (интерфейс) для работы с заказами:
# ✅ ХОРОШО: зависим от абстракции
from abc import ABC, abstractmethod
class OrderRepository(ABC):
@abstractmethod
def get_by_id(self, order_id):
pass
class DjangoOrderRepository(OrderRepository):
def get_by_id(self, order_id):
from .models import Order
return Order.objects.get(id=order_id)
Теперь твоя бизнес-логика зависит от интерфейса
OrderRepository, а не от конкретной модели. И если завтра ты перейдёшь с PostgreSQL на MongoDB или вообще на внешний API — ты просто создашь новую реализацию интерфейса, и всё продолжит работать! 🚀КАК ПРИМЕНИТЬ ЭТО В РЕАЛЬНОМ DJANGO-ПРОЕКТЕ?
1. Выдели интерфейсы для работы с данными. Не таскай
Model.objects.filter() по всему проекту. Создай репозитории или сервисы.2. Используй внедрение зависимостей (DI). Передавай зависимости в конструктор или метод, а не создавай их внутри. В Django для этого часто используют
django-injector или просто передают зависимости через аргументы функций.3. Для сложной бизнес-логики — отдельный слой. Не пиши всё во
views.py. Вынеси логику в services.py, который будет зависеть от абстракций.ЧАСТАЯ ОШИБКА НА СОБЕСЕДОВАНИИ
Многие путают DIP с Dependency Injection (DI). Запомни: DIP — это принцип (ЧТО делать), а DI — это паттерн (КАК делать). DIP говорит «зависи от абстракций», а DI предлагает способ это реализовать — передавать зависимости извне.
ПОЧЕМУ ЭТО СПАСЁТ ТВОЙ ПРОЕКТ?
• Тестируемость. Ты можешь подменить реальный репозиторий на фейковый (mock) в тестах, не трогая базу данных.
• Гибкость. Смена технологий становится простой заменой одной реализации на другую.
• Читаемость. Код становится чище, потому что бизнес-логика не засорена деталями работы с БД.
ИТОГ
DIP — это не просто абстрактная теория из книжек. Это реальный инструмент, который делает твой код на Django гибким, тестируемым и готовым к изменениям. Начни с малого — вынеси работу с моделями в репозитории, и ты почувствуешь разницу.
А теперь вопрос к тебе: сколько мест в твоём проекте напрямую обращаются к
Model.objects? Если больше десяти — пора рефакторить! 🔥#Python #Django #SOLID #DIP #Собеседование #SeniorPython