ru
Feedback
Библиотека Python разработчика | Книги по питону

Библиотека Python разработчика | Книги по питону

Открыть в Telegram

Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq

Больше

📈 Аналитический обзор Telegram-канала Библиотека Python разработчика | Книги по питону

Канал Библиотека Python разработчика | Книги по питону (@bookpython) языкового сегмента Русский является активным участником. Сейчас сообщество объединяет 18 046 подписчиков, занимая 7 037 место в категории Технологии и приложения и 36 158 место в регионе Россия.

📊 Показатели аудитории и динамика

С момента создания невідомо проект демонстрирует стремительный рост, собрав аудиторию из 18 046 подписчиков.

Согласно последним данным от 05 октября, 2026, канал показывает стабильную активность. За последние 30 дней изменение числа участников составило -69, а за последние 24 часа — -4, при этом общий охват остаётся высоким.

  • Статус верификации: Не верифицирован
  • Уровень вовлечённости (ER): Средний показатель вовлечённости аудитории составляет 5.27%. В первые 24 часа после публикации контент обычно набирает 2.51% реакций от общего числа подписчиков.
  • Охват публикаций: В среднем каждый пост получает 951 просмотров. В течение первых суток публикация набирает 453 просмотров.
  • Реакции и взаимодействия: Аудитория активно поддерживает контент: среднее количество реакций на один пост — 4.
  • Тематические интересы: Контент сосредоточен на ключевых темах, таких как numbers, yield, модуль, none, декоратор.

📝 Описание и контентная политика

Автор описывает ресурс как площадку для выражения субъективного мнения:
“Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq”

Благодаря высокой частоте обновлений (последние данные получены 06 октября, 2026) канал поддерживает актуальность и высокий уровень охвата публикаций. Аналитика показывает, что аудитория активно взаимодействует с контентом, что делает его важной точкой влияния в категории Технологии и приложения.

18 046
Подписчики
-424 часа
-247 дней
-6930 дней
Привлечение подписчиков
окт. '26
октябрь '26
+2
в 0 каналах
сентябрь '26
+36
в 0 каналах
Get PRO
август '26
+37
в 0 каналах
Get PRO
июль '26
+35
в 0 каналах
Get PRO
июнь '26
+23
в 0 каналах
Get PRO
май '26
+40
в 0 каналах
Get PRO
апрель '26
+26
в 0 каналах
Get PRO
март '26
+31
в 0 каналах
Get PRO
февраль '26
+62
в 0 каналах
Get PRO
январь '26
+69
в 0 каналах
Get PRO
декабрь '25
+67
в 0 каналах
Get PRO
ноябрь '25
+97
в 31 каналах
Get PRO
октябрь '25
+64
в 1 каналах
Get PRO
сентябрь '25
+90
в 36 каналах
Get PRO
август '25
+60
в 0 каналах
Get PRO
июль '25
+89
в 27 каналах
Get PRO
июнь '25
+116
в 19 каналах
Get PRO
май '25
+119
в 44 каналах
Get PRO
апрель '25
+164
в 37 каналах
Get PRO
март '25
+147
в 38 каналах
Get PRO
февраль '25
+178
в 31 каналах
Get PRO
январь '25
+176
в 33 каналах
Get PRO
декабрь '24
+158
в 34 каналах
Get PRO
ноябрь '24
+112
в 33 каналах
Get PRO
октябрь '24
+139
в 29 каналах
Get PRO
сентябрь '24
+190
в 29 каналах
Get PRO
август '24
+68
в 17 каналах
Get PRO
июль '24
+56
в 0 каналах
Get PRO
июнь '24
+95
в 23 каналах
Get PRO
май '24
+223
в 18 каналах
Get PRO
апрель '24
+174
в 0 каналах
Get PRO
март '24
+240
в 20 каналах
Get PRO
февраль '24
+224
в 18 каналах
Get PRO
январь '24
+287
в 23 каналах
Get PRO
декабрь '23
+253
в 24 каналах
Get PRO
ноябрь '23
+282
в 16 каналах
Get PRO
октябрь '23
+319
в 18 каналах
Get PRO
сентябрь '23
+342
в 0 каналах
Get PRO
август '23
+264
в 0 каналах
Get PRO
июль '23
+292
в 0 каналах
Get PRO
июнь '23
+211
в 0 каналах
Get PRO
май '23
+284
в 0 каналах
Get PRO
апрель '23
+240
в 0 каналах
Get PRO
март '23
+419
в 0 каналах
Get PRO
февраль '23
+147
в 0 каналах
Get PRO
январь '23
+247
в 0 каналах
Get PRO
декабрь '22
+191
в 0 каналах
Get PRO
ноябрь '22
+174
в 0 каналах
Get PRO
октябрь '22
+283
в 0 каналах
Get PRO
сентябрь '22
+268
в 0 каналах
Get PRO
август '22
+287
в 0 каналах
Get PRO
июль '22
+322
в 0 каналах
Get PRO
июнь '22
+233
в 0 каналах
Get PRO
май '22
+258
в 0 каналах
Get PRO
апрель '22
+252
в 0 каналах
Get PRO
март '22
+380
в 0 каналах
Get PRO
февраль '22
+207
в 0 каналах
Get PRO
январь '22
+315
в 0 каналах
Get PRO
декабрь '21
+211
в 0 каналах
Get PRO
ноябрь '21
+269
в 0 каналах
Get PRO
октябрь '21
+326
в 0 каналах
Get PRO
сентябрь '21
+286
в 0 каналах
Get PRO
август '21
+486
в 0 каналах
Get PRO
июль '21
+420
в 0 каналах
Get PRO
июнь '21
+368
в 0 каналах
Get PRO
май '21
+350
в 0 каналах
Get PRO
апрель '21
+387
в 0 каналах
Get PRO
март '21
+568
в 0 каналах
Get PRO
февраль '21
+572
в 0 каналах
Get PRO
январь '21
+777
в 0 каналах
Get PRO
декабрь '20
+18 545
в 0 каналах
Дата
Привлечение подписчиков
Упоминания
Каналы
06 октября0
05 октября0
04 октября0
03 октября0
02 октября+1
01 октября+1
Посты канала
Чтобы сохранить любую информацию в памяти или на устройстве хранения, её необходимо представить в виде байтов. Python, как правило, предоставляет уровень абстракции, при котором вы оперируете непосредственно данными, а не их байтовым представлением. Тем не менее, когда вы, скажем, записываете строку в файл, вы работаете с физической структурой данных. Чтобы поместить символы в файл, их нужно преобразовать в байты — это называется кодированием. Когда вы получаете байты из файла, скорее всего, вы захотите превратить их в осмысленные символы — это называется декодированием. Существует сотни методов кодирования. Самый популярный — вероятно, Unicode, но он сам по себе не может преобразовывать данные в байты. В смысле байтового представления Unicode вообще не является кодировкой. Unicode определяет соответствие между символами и их числовыми кодами. Например, 🐍 имеет код 128013. Но чтобы записать числа в файл, нужна настоящая кодировка. Unicode обычно используется вместе с utf-8, которая (в большинстве случаев) является стандартной в Python. При чтении из файла Python автоматически декодирует utf-8. Вы можете выбрать любую другую кодировку с помощью параметра encoding= функции open, либо читать «сырые» байты, добавив b к режиму открытия файла. 📲 Мы в MAX 👉@BookPython

2
В Python очень короткий список встроенных констант. Одна из них — Ellipsis, которую также можно записать как .... Эта константа не имеет особого значения для интерпретатора, но используется в местах, где такой синтаксис выглядит уместно. Библиотека NumPy поддерживает Ellipsis в качестве аргумента __getitem__, например: x[...] возвращает все элементы массива x. PEP 484 придаёт Ellipsis дополнительное значение: Callable[..., type] — это способ определить тип вызываемых объектов без указания типов аргументов. Наконец, ... можно использовать, чтобы обозначить, что функция ещё не реализована. Это абсолютно валидный код на Python: def x(): ... 📲 Мы в MAX 👉@BookPython
609
3
Обработка исключений в асинхронных программах может быть непростой задачей. В asyncio, если корутина выбрасывает исключение, оно передаётся в тот код, который ожидает соответствующий future. Если await вызывается в нескольких местах, то каждое из них получит это исключение (так как оно сохраняется внутри объекта исключения). Следующий код напечатает сообщение об ошибке пять раз: import asyncio async def error(): await asyncio.sleep(1) raise ValueError() async def waiter(task): try: await task except ValueError: print('error') else: print('OK') async def main(): task = asyncio.get_event_loop().create_task(error()) for _ in range(5): asyncio.get_event_loop().create_task(waiter(task)) await asyncio.sleep(2) loop = asyncio.get_event_loop() loop.run_until_complete(main()) Если исключение выброшено, но задача (task) ни разу не была ожидаема (awaited), исключение будет потеряно. В таком случае при уничтожении задачи вы получите предупреждение: “Task exception was never retrieved”. Когда вы используете await asyncio.gather(tasks) и одна из задач выбрасывает исключение, оно передаётся наружу. Однако если несколько задач выбросят исключения, вы получите только первое, остальные будут проигнорированы: import asyncio async def error(i): await asyncio.sleep(1) raise ValueError(i) async def main(): try: await asyncio.gather( error(1), error(2), error(3), ) except ValueError as e: print(e) loop = asyncio.get_event_loop() loop.run_until_complete(main()) Вы можете использовать gather с параметром return_exceptions=True, чтобы получать исключения как обычные значения. Следующий код напечатает: [42, ValueError(2,), ValueError(3,)] import asyncio async def error(i): await asyncio.sleep(1) if i > 1: raise ValueError(i) return 42 async def main(): results = await asyncio.gather( error(1), error(2), error(3), return_exceptions=True, ) print(results) loop = asyncio.get_event_loop() loop.run_until_complete(main()) 📲 Мы в MAX 👉@BookPython
684
4
Как отлаживать Python-код, не выходя из редактора - с помощью встроенного модуля pdb. 🔍 Быстрая отладка с pdb Часто, когда код не работает как надо, мы начинаем закидывать print()-ами. Но это неудобно, медленно и мусорит код. Вместо этого вставь в нужное место строчку: import pdb; pdb.set_trace() Когда выполнение дойдет до этой строки, ты попадешь в интерактивную консоль отладчика прямо в терминале. Дальше можно: - n (next) — перейти к следующей строке; - s (step) — зайти внутрь функции; - c (continue) — продолжить выполнение; - l (list) — показать текущий контекст; - p var — вывести значение переменной var. 💡 Пример def calc(a, b): import pdb; pdb.set_trace() result = a + b return result calc(2, 3) На строке с pdb.set_trace() ты остановишься и сможешь изучить, что происходит внутри. Зачем это нужно? - Понять, почему что-то идет не так. - Посмотреть, какие значения у переменных прямо в момент ошибки. - Быстро отладить без запуска IDE - удобно в Docker, SSH или при работе с cron. Попробуй - один раз освоишь, и уже не захочешь возвращаться к print(). 📲 Мы в MAX 👉@BookPython
813
5
Python поддерживает цепочные присваивания со следующим синтаксисом: a = b = c = 42 Выглядит как цепочка присваиваний в C, но работает совершенно иначе. В C результат одного присваивания используется в следующем: a = (b = (c = 42)) В Python всё не так. Операция присваивания не возвращает результат — это оператор, а не выражение. Вместо этого происходит несколько присваиваний слева направо: 2 0 LOAD_CONST 1 (42) 2 DUP_TOP 4 STORE_FAST 0 (a) 6 DUP_TOP 8 STORE_FAST 1 (b) 10 STORE_FAST 2 (c) 📲 Мы в MAX 👉@BookPython
924
6
Чтобы объект можно было использовать в качестве ключа словаря, он должен быть хешируемым. Хешируемые объекты поддерживают метод __hash__, который возвращает целое число. Для получения хеша значения используется встроенная функция hash. Встроенные типы, которые являются неизменяемыми, по умолчанию хешируемы. Все пользовательские объекты тоже хешируемы, но есть нюанс. Если вы определяете метод __eq__ для своего типа, то вы также должны определить __hash__ таким образом, чтобы hash(a) == hash(b) для всех a и b, которые считаются равными. Нарушение этого правила может привести к некорректной работе словаря: class A: def __init__(self, x): self.x = x def __hash__(self): return random.randrange(10000) def __eq__(self, other): return self.x == other.x d = {} d[A(2)] = 2 d.get(A(2), 0) # Вывод: 0 Обратите внимание: как только вы определяете __eq__ в классе, реализация __hash__ по умолчанию удаляется, так как она больше не подходит (по умолчанию все значения считаются неравными). 📲 Мы в MAX 👉@BookPython
927
7
Как быстро очистить список в Python? Сегодня я покажу вам несколько способов очистки списка в Python и расскажу, какой вариант лучше в зависимости от ситуации. 1️⃣ Присвоение пустого списка lst = [1, 2, 3, 4, 5] lst = [] Этот способ создаёт новый пустой список, но старая ссылка остаётся в памяти, если на неё есть другие ссылки. 2️⃣ Использование .clear() lst = [1, 2, 3, 4, 5] lst.clear() Метод .clear() очищает список на месте, не создавая новый объект. Это предпочтительный способ, если список используется в нескольких местах. 3️⃣ Использование del lst = [1, 2, 3, 4, 5] del lst[:] Работает аналогично .clear(), но выглядит чуть менее очевидно. 4️⃣ Удаление списка полностью lst = [1, 2, 3, 4, 5] del lst Этот вариант полностью удаляет переменную lst. Если потом попробовать к ней обратиться, будет ошибка NameError. 🔹 Какой способ лучше? • Если нужно просто очистить список, используйте .clear(). • Если хотите заменить его новым объектом - lst = []. • del lst[:] – редкий вариант, но возможен. • del lst подходит, если список больше не нужен в программе. Какой вариант используете вы? Пишите в комментариях! 📲 Мы в MAX 👉@BookPython
1 006
8
🚀 5 крутых Python-фишек, о которых знают не все Python — мощный и удобный язык, но даже опытные разработчики иногда не знают всех его возможностей. Сегодня я покажу пять полезных трюков, которые могут упростить вашу работу. 1. Используем else в for и while Многие не знают, что в Python циклы for и while могут иметь блок else. Он выполняется, если цикл не был прерван через break. ✅ Пример: numbers = [1, 3, 5, 7] for num in numbers: if num % 2 == 0: print("Есть чётное число!") break else: print("Чётных чисел нет.") 🔹 Если в списке нет чётных чисел, сработает else. 2. "Распаковка" переменных В Python можно присваивать сразу несколько значений одной строкой. ✅ Пример: a, b, c = 1, 2, 3 print(a, b, c) # 1 2 3 Можно менять местами значения без временной переменной: x, y = 5, 10 x, y = y, x print(x, y) # 10 5 3. Используем _ в больших числах Чтобы числа легче читались, можно разделять разряды _. ✅ Пример: big_number = 1_000_000_000 print(big_number) # 1000000000 Это просто синтаксический сахар, Python игнорирует _ при вычислениях. 4. Получаем значение из словаря с запасным вариантом Вместо if key in dict можно использовать .get(), чтобы избежать KeyError. ✅ Пример: user_data = {"name": "Alice"} age = user_data.get("age", 18) # Если ключа "age" нет, вернётся 18 print(age) # 18 5. "Распаковка" списка в аргументы функции Оператор * позволяет передавать элементы списка в функцию как отдельные аргументы. ✅ Пример: def greet(name, age): print(f"Привет, {name}! Тебе {age} лет.") user_info = ["Иван", 25] greet(*user_info) # Привет, Иван! Тебе 25 лет. То же работает со словарями через **: user_dict = {"name": "Ольга", "age": 30} greet(**user_dict) Эти фишки делают код лаконичнее и понятнее. 📲 Мы в MAX 👉@BookPython
792
9
🔥 Как ускорить Python-код в 10 раз? Сегодня покажу вам мощный инструмент для оптимизации кода - Numba. Это библиотека, которая позволяет компилировать Python-функции в машинный код, используя JIT-компилятор. 🚀 Как это работает? Numba использует LLVM для компиляции кода во время выполнения, превращая Python в код, близкий по скорости к C. Пример Допустим, у нас есть функция, вычисляющая сумму квадратов чисел: import numpy as np import time def sum_of_squares(n): result = 0 for i in range(n): result += i ** 2 return result n = 10**7 start = time.time() sum_of_squares(n) print("Обычный Python:", time.time() - start) Теперь ускорим её с помощью Numba: from numba import jit @jit(nopython=True) def sum_of_squares_numba(n): result = 0 for i in range(n): result += i ** 2 return result start = time.time() sum_of_squares_numba(n) print("С Numba:", time.time() - start) 📊 Результат: ✅ Код на чистом Python выполняется ~5-10 раз медленнее, чем с Numba. ✅ Numba особенно полезна для математических вычислений и обработки массивов. ✅ Простая аннотация @jit(nopython=True) уже даёт мощный прирост скорости! Где применять? ✔ Численные расчёты ✔ Обработку данных ✔ Алгоритмы машинного обучения Попробуйте Numba и напишите в комментариях, удалось ли вам ускорить свой код! 📲 Мы в MAX 👉@BookPython
819
10
10 полезных библиотек для Python-разработчика Сегодня я подготовил для вас подборку из 10 полезных библиотек, которые могут сделать вашу разработку проще и эффективнее. Поехали! 🚀 1️⃣ Rich – красивый вывод в консоль. Если хотите раскрасить логи, добавить таблицы или анимации – эта библиотека вам пригодится. 📌 pip install rich 2️⃣ Typer – современный способ писать CLI-приложения. Работает на основе аннотаций типов и делает разработку CLI удобнее. 📌 pip install typer 3️⃣ Pendulum – альтернатива datetime, но с удобным API и встроенной поддержкой часовых поясов. 📌 pip install pendulum 4️⃣ HTTPX – асинхронный клиент для работы с HTTP-запросами. Поддерживает async/await, в отличие от requests. 📌 pip install httpx 5️⃣ Pydantic – мощный инструмент для валидации данных и работы с моделями. Особенно полезен в FastAPI. 📌 pip install pydantic 6️⃣ Poetry – современный менеджер зависимостей. Упрощает работу с виртуальными окружениями и пакетами. 📌 pip install poetry 7️⃣ Loguru – удобная альтернатива стандартному logging. Позволяет логировать без лишнего кода. 📌 pip install loguru 8️⃣ FastAPI – один из самых быстрых Python-фреймворков для создания API. Использует аннотации типов и async/await. 📌 pip install fastapi 9️⃣ Tqdm – библиотека для удобных progress-bar'ов в терминале. Незаменима при обработке больших данных. 📌 pip install tqdm 🔟 Black – автоматический форматтер кода, который придерживается строгого стиля. Просто устанавливаешь – и больше не думаешь о стиле кода. 📌 pip install black 📲 Мы в MAX 👉@BookPython
970
11
Если у вас есть ресурсоемкая задача для процессора и вы хотите использовать все доступные ядра, то multiprocessing.Pool - это то, что вам нужно. Он создает несколько процессов и автоматически распределяет между ними задачи. Просто создайте пул с Pool(number_of_processes) и выполните p.map с списком входных данных. import math from multiprocessing import Pool inputs = [i ** 2 for i in range(100, 130)] def f(x): return len(str(math.factorial(x))) # Однопоточное выполнение %timeit [f(x) for x in inputs] # 1.44 s ± 19.2 ms per loop (...) # Параллельное выполнение с 4 процессами p = Pool(4) %timeit p.map(f, inputs) # 451 ms ± 34 ms per loop (...) Также можно не указывать параметр number_of_processes, по умолчанию он равен количеству ядер CPU в системе. 📲 Мы в MAX 👉@BookPython
1 195
12
Protocol vs ABC: Утиная типизация на стероидах (Static Duck Typing) В классическом ООП (Java, C#) и при использовании abc.ABC в Python мы привыкли к Nominal Subtyping (Именная подтипизация). Чтобы объект считался Bird, он должен явно наследоваться от Bird. Это создает жесткую связность (coupling): ваша реализация должна знать об интерфейсе и импортировать его. С приходом typing.Protocol (Python 3.8+) мы получили Structural Subtyping (Структурная подтипизация). В чем суть? Если класс имеет метод quack(), то это Утка. Неважно, от чего он наследуется. Это и есть та самая «утиная типизация», но теперь поддерживаемая статическим анализатором (mypy, pyright, IDE). Сравним код: ❌ Старый путь (ABC): from abc import ABC, abstractmethod # 1. Жестко определяем интерфейс class SenderABC(ABC): @abstractmethod def send(self, msg: str) -> None: pass # 2. Обязаны наследоваться! class EmailService(SenderABC): def send(self, msg: str) -> None: print(f"Email: {msg}") def alert(sender: SenderABC): sender.send("Alert!") ✅ Новый путь (Protocol): from typing import Protocol # 1. Описываем, "что мы ждем от объекта" class SenderProto(Protocol): def send(self, msg: str) -> None: ... # 2. Реализация НИЧЕГО не знает про Protocol # Никаких импортов и наследования! class SmsService: def send(self, msg: str) -> None: print(f"SMS: {msg}") # Mypy счастлив: SmsService имеет нужную структуру (метод send) def alert(sender: SenderProto): sender.send("Alert!") alert(SmsService()) Киллер-фича: Retroactive Abstraction (Ретроактивная абстракция) Представьте, что вы используете стороннюю библиотеку (например, boto3 или клиент Redis). Вы не можете заставить их классы наследоваться от ваших ABC. С помощью Protocol вы можете создать интерфейс для уже существующего чужого кода, не меняя его, и типизировать свои функции. Нюансы для Middle+: 1. Runtime: По умолчанию isinstance(obj, MyProtocol) выбросит ошибку. Протоколы - это compile-time фича. Если нужна проверка в рантайме, декорируйте протокол @runtime_checkable. 2. Свойства: В протоколе можно описывать не только методы, но и поля через @property или просто аннотации типов. Используйте Протоколы, чтобы развязать зависимости между модулями. Это основа принципа Dependency Inversion в Python. #python #typing #mypy #architecture #clean_code 📲 Мы в MAX 👉@BookPython
1 074
13
⚡ Как ускорить код на Python? Используем map, filter, reduce ⚡ Привет, друзья! Сегодня расскажу о том, как можно ускорить выполнение кода, заменяя обычные циклы на встроенные функции map(), filter() и reduce(). Эти инструменты позволяют писать более компактный, читаемый и быстрый код. ✅ map() Функция map() применяется к каждому элементу последовательности и возвращает новый итератор. ❌ Обычный способ: numbers = [1, 2, 3, 4, 5] squared = [] for num in numbers: squared.append(num ** 2) ✅ Быстрее с map(): numbers = [1, 2, 3, 4, 5] squared = list(map(lambda x: x ** 2, numbers)) За счёт того, что map() использует C-оптимизированную логику, код выполняется быстрее. ✅ filter() Фильтрует элементы последовательности по заданному условию. ❌ Медленный вариант: numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = [] for num in numbers: if num % 2 == 0: evens.append(num) ✅ Быстрее с filter(): numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = list(filter(lambda x: x % 2 == 0, numbers)) Такой код читается легче и работает быстрее. ✅ reduce() Позволяет выполнять кумулятивные операции (например, суммирование, умножение). ❌ Классический способ: numbers = [1, 2, 3, 4, 5] product = 1 for num in numbers: product *= num ✅ Быстрее с reduce(): from functools import reduce numbers = [1, 2, 3, 4, 5] product = reduce(lambda x, y: x * y, numbers) Этот метод полезен, если нужно свести список к одному значению. 💡 Важно: reduce() чаще заменяют sum() или math.prod(), но для сложных операций он остаётся полезным. Такие функции помогают писать код, который не только быстрее работает, но и легче читается. 📲 Мы в MAX 👉@BookPython
866
14
Ловушка замыканий: Почему ваши лямбды в цикле сломаны (Late Binding) Вы пишете код, который генерирует список функций (например, колбэки для кнопок в UI или динамические фильтры). Кажется, что все логично, но на выходе получаете сюрприз. Проблемный код: # Хотим создать 3 функции, которые возвращают 0, 1 и 2 соответственно funcs = [] for i in range(3): funcs.append(lambda: i) # Проверяем results = [f() for f in funcs] print(results) # Ожидание: [0, 1, 2] # Реальность: [2, 2, 2] Почему так происходит? Это называется Late Binding (позднее связывание). В Python замыкания (closures) захватывают переменные по ссылке, а не по значению. Когда вы объявляете lambda: i, Python не сохраняет текущее число 0, 1 или 2. Он сохраняет инструкцию: «когда меня вызовут, пойди в локальную область видимости, найди переменную с именем i и возьми ее значение». К моменту, когда вы начинаете вызывать функции из списка results, цикл for уже завершился. Переменная i в этой области видимости навсегда осталась равной 2. Все три лямбды смотрят на одну и ту же переменную i. Как лечить? Есть два каноничных способа заставить Python запомнить значение «здесь и сейчас». 1. Аргумент по умолчанию (Hack way) Значения аргументов по умолчанию вычисляются в момент определения функции. funcs = [] for i in range(3): # i=i создает локальную переменную i внутри функции # и присваивает ей текущее значение i из цикла funcs.append(lambda i=i: i) Это работает быстро, но выглядит немного грязно и может сбить с толку линтеры или коллег. 2. functools.partial (Enterprise way) Более чистый и явный способ. partial создает новый callable-объект, «замораживая» переданные аргументы. from functools import partial funcs = [] for i in range(3): # Здесь значение i фиксируется жестко funcs.append(partial(lambda x: x, i)) Где это стреляет в реальной жизни? - Генерация command для кнопок в Tkinter/PyQt. - Динамическое создание task в asyncio циклах. - Патчинг тестов в циклах. Не дайте переменным пережить свое время. #python #internals #functionalprogramming #gotchas 📲 Мы в MAX 👉@BookPython
927
15
Профилируем Python в продакшене: почему cProfile не подходит, и чем хорош py-spy Когда на проде начинает течь память или скачет CPU, первая мысль, подключить профайлер. Стандартный cProfile, это детерминированный профайлер. Он хукает каждый вызов функции. 📉 Цена: Оверхед может замедлить приложение в 2-5 раз. На нагруженном проде это означает добить сервис окончательно. Для Live-систем нужен Sampling Profiler (сэмплирующий профайлер). Золотой стандарт сейчас - py-spy. Как это работает? py-spy написан на Rust. Он работает как внешний процесс, который читает память вашего Python-процесса (через системные вызовы, аналогично gdb). Он делает «снимки» стека вызовов с высокой частотой (по дефолту 100 раз в секунду). ✅ Результат: Оверхед стремится к нулю. Код инструментализировать не нужно. Рестарт сервиса не нужен. Два главных режима работы: 1. Live View (как htop, но для функций) Посмотреть в реальном времени, в каких функциях процесс проводит больше всего времени. # Нужно только знать PID процесса py-spy top --pid 12345 Вы увидите список функций, отсортированный по OwnTime (время внутри функции) и TotalTime (время с учетом дочерних вызовов). 2. Flame Graph (Огненный граф) Для глубокого анализа лучше записать работу сервиса за период и визуализировать стек. py-spy record -o profile.svg --pid 12345 --duration 60 Вы получите SVG-файл. Чем шире полоска, тем больше времени занимает функция. Вертикаль - это глубина стека. Сразу видно, кто «съел» процессорное время. Нюансы для Middle+: - GIL: py-spy умеет показывать, держит ли функция GIL. Добавьте флаг --gil. - Docker/K8s: Так как py-spy использует системный вызов ptrace, контейнеру нужны привилегии. В Kubernetes часто нужно добавить securityContext: capabilities: add: ["SYS_PTRACE"] подам, чтобы иметь возможность профилировать их на лету. #profiling #optimization #pyspy #debugging #python 📲 Мы в MAX 👉@BookPython
863
16
📌 Декораторы в Python: как они работают и зачем нужны? Сегодня я покажу вам, как работают декораторы в Python и зачем они вообще нужны. Декораторы позволяют изменять поведение функций без изменения их кода. Они широко применяются в логировании, кешировании, управлении доступом и многом другом. Допустим, у нас есть функция, которая просто выводит «Hello, world!»: def greet(): print("Hello, world!") Теперь мы хотим, чтобы перед выполнением этой функции выполнялся какой-то код, например, логирование. Вместо изменения greet(), мы создадим декоратор: def log_decorator(func): def wrapper(): print(f"Вызов функции {func.__name__}") return func() return wrapper И теперь используем его: @log_decorator def greet(): print("Hello, world!") greet() 👉 Вывод: Вызов функции greet Hello, world! Как это работает? 1. Декоратор принимает функцию (func) в качестве аргумента. 2. Внутри создаётся вложенная функция wrapper(), которая выполняет дополнительную логику перед вызовом func(). 3. wrapper() возвращается вместо func, фактически подменяя её. Можно даже передавать аргументы в декорируемую функцию: def log_decorator(func): def wrapper(*args, **kwargs): print(f"Вызов {func.__name__} с аргументами: {args}, {kwargs}") return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): return a + b print(add(3, 5)) 👉 Вывод: Вызов add с аргументами: (3, 5), {} 8 🔥 Декораторы это мощный инструмент, который делает код чище и удобнее. Если ещё не использовали их в проектах, самое время попробовать! 📲 Мы в MAX 👉@BookPython
840
17
🐍 Как улучшить читаемость кода в Python? Читаемый код - это не роскошь, а необходимость. Если ваш код трудно понять, даже если он работает, это плохой код. Сегодня разберем несколько простых, но мощных приемов, которые сделают ваш код более понятным. 1️⃣ Используйте говорящие имена переменных Плохой пример: a = 10 b = 20 c = a + b Хороший пример: price = 10 tax = 20 total_cost = price + tax Теперь сразу понятно, что делает код! 2️⃣ Разбивайте код на функции Вместо длинных кусков кода, используйте функции: def calculate_total(price, tax): return price + tax total_cost = calculate_total(10, 20) Теперь код можно переиспользовать и проще тестировать. 3️⃣ Следуйте PEP 8 Форматирование кода влияет на его читаемость. Например, пробелы вокруг операторов делают код более понятным: # Плохо total=price+tax # Хорошо total = price + tax Пользуйтесь black или flake8, чтобы следить за стилем. 4️⃣ Избегайте магических чисел Если в коде встречаются непонятные числа, лучше заменить их на константы: # Плохо if age > 18: print("Взрослый") # Хорошо LEGAL_AGE = 18 if age > LEGAL_AGE: print("Взрослый") 5️⃣ Используйте list comprehensions Вместо: numbers = [1, 2, 3, 4, 5] squared_numbers = [] for num in numbers: squared_numbers.append(num ** 2) Лучше: squared_numbers = [num ** 2 for num in numbers] Чище и лаконичнее! Читаемый код делает разработку приятнее, ускоряет исправление багов и упрощает поддержку. Напишите в комментариях, какие еще приемы вы используете для улучшения читаемости кода! 👇 📲 Мы в MAX 👉@BookPython
792
18
Списковые включения (list comprehensions) могут содержать несколько операторов for и if: In : [(x, y) for x in range(3) for y in range(3)] Out: [ (0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2) ] Можно также добавлять условия if для фильтрации значений: In : [ (x, y) for x in range(3) for y in range(3) if x != 0 if y != 0 ] Out: [(1, 1), (1, 2), (2, 1), (2, 2)] Любое выражение с for и if может использовать все переменные, определённые ранее: In : [ (x, y) for x in range(3) for y in range(x + 2) if x != y ] Out: [ (0, 1), (1, 0), (1, 2), (2, 0), (2, 1), (2, 3) ] Вы можете комбинировать for и if в любом порядке: In : [ (x, y) for x in range(5) if x % 2 for y in range(x + 2) if x != y ] Out: [ (1, 0), (1, 2), (3, 0), (3, 1), (3, 2), (3, 4) ] 📲 Мы в MAX 👉@BookPython
980
19
Structured Concurrency в Python 3.11+ (TaskGroup vs Gather) Если вы до сих пор используете asyncio.gather() для запуска конкурентных задач, вы, вероятно, теряете контроль над ошибками. Главная проблема gather: если одна таска падает, остальные продолжают работать (если не стоит return_exceptions=False, который убивает всё сразу, но "грязно"). Плюс, отлавливать несколько ошибок одновременно через try / except - это боль. В Python 3.11+ (и через trio / anyio раньше) стандартом стала Structured Concurrency через asyncio.TaskGroup. Как это выглядит: import asyncio async def task_fail(name, sec): await asyncio.sleep(sec) raise ValueError(f"Error in {name}") async def task_ok(name, sec): await asyncio.sleep(sec) print(f"Task {name} done") async def main(): try: async with asyncio.TaskGroup() as tg: tg.create_task(task_ok("A", 1)) tg.create_task(task_fail("B", 2)) # Упадет через 2 сек tg.create_task(task_fail("C", 1.5)) # Упадет через 1.5 сек except* ValueError as eg: # Обратите внимание на except* (ExceptionGroup) print(f"Caught errors: {eg.exceptions}") # Запуск # asyncio.run(main()) Почему TaskGroup это выбор Middle+: 1. Атомарность скоупа: Если одна задача внутри контекстного менеджера падает, TaskGroup автоматически отменяет (cancel) все остальные запущенные задачи в группе. Вы не оставляете "зомби-процессы" в фоне. 2. Exception Groups: Если упало несколько задач одновременно (или во время отмены), Python соберет их в ExceptionGroup. Конструкция except* позволяет элегантно обрабатывать дерево исключений. Вывод: Для скриптов gather сойдет. Для надежных сервисов, где важна консистентность состояния и корректный шатдаун корутин - переезжайте на TaskGroup. #asyncio #python311 #bestpractices #concurrency 📲 Мы в MAX 👉@BookPython
960
20
Оптимизация SQL-запросов в Django ORM Сегодня я покажу вам, как оптимизировать SQL-запросы в Django ORM, чтобы ваш код работал быстрее и эффективнее. Если ваш Django-проект начал тормозить, скорее всего, проблема в количестве и сложности запросов к базе данных. 1️⃣ Используйте select_related и prefetch_related Django ORM лениво загружает связанные объекты, что может привести к множественным SQL-запросам (N+1). Вместо этого используйте: # select_related — жадная загрузка (для ForeignKey, OneToOne) posts = Post.objects.select_related("author").all() # prefetch_related — для ManyToMany и Reverse ForeignKey posts = Post.objects.prefetch_related("comments").all() Это значительно уменьшает количество запросов к базе. 2️⃣ Используйте only и defer Если вам не нужны все поля модели, загружайте только необходимые: users = User.objects.only("id", "username") # Загружаем только ID и имя А если хотите исключить несколько полей: users = User.objects.defer("bio", "last_login") # Исключаем ненужные поля 3️⃣ Агрегация вместо перебора в Python Вместо: total_likes = sum(post.likes.count() for post in posts) Используйте annotate: from django.db.models import Count posts = Post.objects.annotate(total_likes=Count("likes")) Это выполнится на стороне базы, а не в Python, что намного быстрее. 4️⃣ Используйте exists() вместо count() Если вам нужно проверить, есть ли записи в базе, не используйте count(), это дорогостоящий запрос: if User.objects.filter(email="test@example.com").exists(): # Быстро ❌ Плохо: if User.objects.filter(email="test@example.com").count() > 0: # Долго 5️⃣ Кешируйте запросы Django поддерживает кеширование, и если запросы повторяются, можно использовать: from django.core.cache import cache users = cache.get("users") if not users: users = list(User.objects.all()) # Загружаем пользователей cache.set("users", users, timeout=60 * 15) # Кешируем на 15 минут Эти простые приемы помогут вам ускорить Django-приложение и уменьшить нагрузку на базу данных. А вы уже используете их в своих проектах? Делитесь в комментариях! 👇 📲 Мы в MAX 👉@BookPython
843