Zen of Python
Полный Дзен Пайтона в одном канале Разместить рекламу: @tproger_sales_bot Правила общения: https://tprg.ru/rules Другие каналы: @tproger_channels Сайт: https://tprg.ru/site Регистрация в перечне РКН: https://tprg.ru/xZOL
Show more📈 Analytical overview of Telegram channel Zen of Python
Channel Zen of Python (@zen_of_python) in the Russian language segment is an active participant. Currently, the community unites 18 730 subscribers, ranking 6 999 in the Technologies & Applications category and 35 209 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 18 730 subscribers.
According to the latest data from 26 October, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -87 over the last 30 days and by -8 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 6.18%. Within the first 24 hours after publication, content typically collects 4.24% reactions from the total number of subscribers.
- Post reach: On average, each post receives 1 158 views. Within the first day, a publication typically gains 795 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 1.
- Thematic interests: Content is focused on key topics such as github, rust, pip, api, install.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Полный Дзен Пайтона в одном канале
Разместить рекламу: @tproger_sales_bot
Правила общения: https://tprg.ru/rules
Другие каналы: @tproger_channels
Сайт: https://tprg.ru/site
Регистрация в перечне РКН: https://tprg.ru/xZOL”
Thanks to the high frequency of updates (latest data received on 27 October, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
time-machine, замерил 3 августа, как две библиотеки подмены текущего времени ведут себя с ростом проекта. Прошлый такой замер он делал в 2021 году и получил разницу в 100–200 раз, теперь захотел показать не отношение, а саму зависимость от размера кодовой базы.
Стенд простой: генерируются пустые модули по 25 атрибутов, каждый десятый держит ссылки на date, datetime и time, как будто их импортировали по имени. Замеряется цикл включения и выключения подмены. Python 3.15, MacBook на M1, freezegun 1.5.5 и time-machine 3.3.0.
🔘 259 модулей, то есть почти голый интерпретатор: 1406,7 мкс против 1,5 мкс, разница в 940 раз;
🔘 1259 модулей, размер небольшого проекта на Django: разница в 2490 раз;
🔘 16 259 модулей, крупный проект с обвесом зависимостей: 40 971,3 мкс против неизменных 1,5 мкс, разница в 27 300 раз;
🔘 время freezegun укладывается в прямую: около 1,4 мс постоянных расходов плюс 2,5 мкс на каждый модуль;
🔘 в наборе из 2000 тестов с подменой времени это 82 секунды чистой замены ссылок против 3 миллисекунд;
🔘 причина в том, что freeze_time.start() обходит весь sys.modules и подменяет каждую найденную ссылку на настоящие функции; даже при попадании в кэш приходится перечислить и захешировать имена атрибутов всех модулей.
У обхода есть и содержательная плата, помимо скорости: он не находит ссылки в атрибутах классов, значениях по умолчанию, замыканиях и C-расширениях, и они продолжают отдавать настоящее время. Плюс подставленные объекты видны по типу, datetime.__name__ внутри подмены становится FakeDatetime, и код, который смотрит на типы, может повести себя иначе.
time-machine вместо обхода переписывает указатель ml_meth в структуре PyMethodDef у встроенных функций, читающих часы. Таких функций десять, и это ровно десять записей в память независимо от размера проекта.
Автор оговаривает, что модули в стенде синтетические, это types.ModuleType, положенные прямо в sys.modules, а не настоящие импорты, и что замеряется только цикл подмены, из нескольких прогонов берётся минимальное время.
Полная статья: https://adamj.eu/tech/2026/08/03/python-time-machine-o1-freezegun-on/
@zen_of_pythonThreadPoolExecutor заметно медленнее того же расчёта через ProcessPoolExecutor. Кумар Адитья из Quansight Labs описал 29 июля, как несколько месяцев вычищал причины этого в NumPy и самом CPython.
Нагрузка простая и типичная для ufunc: каждый работник берёт свой массив, гоняет по нему np.sin и np.cos в цикле и сворачивает результат. Общего изменяемого состояния между потоками нет, так что масштабироваться оно должно линейно. На деле до 18 потоков росло, а дальше резко деградировало: на 32 работниках 44 секунды против 6 у процессов. Профилирование через samply показало три класса проблем: конкуренция за блокировки, конкуренция за счётчики ссылок общих объектов и конкуренция в аллокаторе.
🔘 tracemalloc выключен по умолчанию, но всё равно брал глобальную блокировку на каждом выделении и освобождении памяти, просто чтобы проверить, включён ли он. Теперь проверка идёт атомарной операцией без блокировки;
🔘 кэш диспетчеризации ufunc, который сопоставляет типам аргументов конкретную реализацию, жил под std::shared_mutex. Записи в нём неизменяемы, поэтому чтение сделали полностью свободным от блокировок, мьютекс остался только на редкие вставки;
🔘 указатель на аллокатор памяти NumPy хранит в глобальном объекте PyCapsule. Без GIL каждое обращение к нему меняет счётчик ссылок атомарно, и кэш-линия со счётчиком начинает метаться между ядрами. Объект сделали бессмертным, то есть вообще без подсчёта ссылок;
🔘 ради этого в CPython появился публичный PyUnstable_SetImmortal: с 3.15 он доступен всем, на 3.14 его можно взять через pythoncapi-compat;
🔘 запись np.sin — это поиск атрибута в модуле, а специализация байткода для таких поисков не работала, если модуль определяет __getattr__. Каждый вызов уходил на медленный путь с захватом импортной блокировки;
🔘 массивы NumPy выделял системным malloc, который плохо переносит параллельные выделения, особенно на macOS. Сырой аллокатор CPython в сборке без GIL перевели на mimalloc, а NumPy переключили на этот сырой аллокатор.
После всех правок та же задача на 32 ядрах занимает около 1,5 секунды: примерно в 30 раз быстрее, чем было, и вчетверо быстрее варианта с процессами. Автор оговаривает, что замеры сделаны на одной 32-ядерной машине с Linux и на конкретной ufunc-нагрузке без общего состояния между потоками.
Пробовали сборку без GIL на своих задачах и во что упирались?
Полная статья: https://labs.quansight.org/blog/scaling-numpy-on-free-threaded-python
@zen_of_pythonlazy import json и lazy from json import dumps. Модуль не грузится, пока имя реально не тронули. Строго opt-in: меняется поведение только помеченных строк. Главные бенефициары — CLI-утилиты и тест-сьюты с тяжёлым деревом зависимостей.
— PEP 686, UTF-8 по умолчанию. Кодировка для I/O больше не зависит от системной локали. Откатить можно через PYTHONUTF8=0 или -X utf8=0 — и вот это стоит проверить заранее, если у вас Windows и legacy-файлы в cp1251.
— PEP 814, встроенный frozendict. Наконец-то неизменяемый словарь в ядре, а не в трёх конкурирующих пакетах на PyPI.
— PEP 798, распаковка в comprehensions. [*L for L in lists] для плоского списка, {**d for d in dicts} для слияния словарей — синтаксическая дыра, которая раздражала лет десять.
— PEP 799, семплирующий профайлер Tachyon прямо в стандартной библиотеке. Другой класс инструмента, чем cProfile: тот инструментирует каждый вызов и искажает картину на горячем коде, семплирующий периодически снимает стек и почти не мешает.
— JIT прибавил 6–7% среднего геометрического на x86-64 Linux и 12–13% на AArch64 macOS.
Плюс по мелочи: sentinel из PEP 661, TypedDict с типизированными extra-полями (PEP 728), TypeForm (PEP 747) и более внятные подсказки в AttributeError.
Полный список — в whatsnew, там же примеры кода и раздел с несовместимостями.
#python315 #cpythonENTER_TRACING() и LEAVE_TRACING();
🔘 в игрушечном замере медиана по 40 запускам составила 1,72 микросекунды без записи и 7,47 микросекунды с записью и JIT.
Автор оценивает накладные расходы максимум в 4,5x и сразу оговаривает, что сравнивать это с накладными расходами 900–1000x у PyPy некорректно.
Чем профилируете горячий Python-код сейчас и во сколько раз он от этого замедляется?
@zen_of_pythonhtml.parser и 836 мкс у html5lib. На плотном четырёхмегабайтном тексте escape отрабатывает за 4,98 мс против 12,7 мс у html.escape. Публикация от 18 июня обновлена 10 июля.
Скорость складывается из того, как обрабатываются байты. Обычный код ищет спецсимволы посимвольно, и процессор на каждом байте выполняет ветвление. Здесь байты обрабатываются пачками: SWAR складывает восемь байт в одно машинное слово и проверяет их арифметикой, SIMD делает то же самое сразу по шестнадцать байт одной инструкцией. Ветвлений в горячем цикле почти не остаётся.
🔘 длина результата сначала считается точно, потом выделяется одним куском и заполняется массовым копированием, вместо дописывания по мере разбора;
🔘 токенизатор сохраняет исходную ширину строки и компилируется в три варианта под UCS-1, UCS-2 и UCS-4;
🔘 чистые текстовые куски возвращаются как срезы без копирования, копия делается только когда она правда нужна;
🔘 переписывание css_path() с квадратичного перебора на индекс сократило пример со 112 мс до 0,9 мс;
🔘 список переиспользуемых обёрток ускорил find_all() с 1,9 до 1,4 мкс, но в сборке без GIL он отключён.
Автор оговаривает, что Callgrind считает инструкции, а не реальное время по часам, поэтому на него опираются только при сравнении вариантов. Корректность проверяется побайтовым сравнением с эталонными библиотеками, сборками с ASan и UBSan и фаззингом.
Что у вас парсит HTML в проде и сколько времени на это уходит?
@zen_of_pythonx = [] и x.append(1) pyright, Basedpyright и ty считают тип list[Unknown], а mypy, Pyrefly и Zuban выводят list[int].
Автор предупреждает, что процент прохождения тестов на соответствие не означает практической пользы: пункты в наборе неравноценны по важности, а инструменты в бете меняются быстро, так что цифры стареют.
Переезжали с mypy на что-то из нового поколения, и сколько чужих ошибок вылезло на неаннотированном коде?
@zen_of_pythonuv init теперь создаёт проект со сборочной конфигурацией
Astral выпустила uv 0.12.0 28 июля, первый крупный релиз после 0.11.0 в марте. Изменения касаются дефолтов инициализации, проверки хэшей и разбора архивов пакетов.
Раньше uv init создавал плоскую структуру без сборочной системы, и сборочную конфигурацию со структурой src приходилось добавлять отдельно. Теперь проект сразу пригоден для сборки.
🔘 uv init прописывает [build-system] на uv_build, кладёт код в src/<name> и добавляет точку входа в [project.scripts], прежнее поведение возвращает флаг --no-package;
🔘 отклоняются исходные дистрибутивы в форматах .tar.bz2 и .tar.xz, по PEP 625 допустим .tar.gz, старые .zip пока принимаются ради совместимости; заодно запрещены записи со сжатием bzip2 и LZMA внутри wheel;
🔘 wheel, способный перезаписать сам интерпретатор (файлы вида Python, python.py, Python.exe, записи в .data/scripts), больше не устанавливается;
🔘 режим предрелизов по умолчанию сменился с if-necessary-or-explicit на if-necessary, то есть предрелизная версия ставится только когда без неё зависимости не разрешаются;
🔘 директива --require-hashes внутри requirements.txt действительно включает проверку хэшей, а хэши только по MD5 отвергаются, нужен минимум SHA-256;
🔘 строже валидируется pylock.toml: обязателен массив packages, имя файла допустимо только вида pylock.toml или pylock.dev.toml, проверяется размер артефактов.
Авторы пишут, что большинству обновление не потребует правок. Исключение составляют проекты, где зафиксирована верхняя граница сборочного бэкенда: там нужно поднять до uv_build>=0.11.32,<0.13.
У вас в CI uv закреплён конкретной версией или подтягивается последний?
@zen_of_pythongc.collect() честно собирал циклический мусор, а RSS после него почти не опускался. Сакшам Шарма разобрал этот случай 8 июля.
Дело в разделении обязанностей. Сборщик мусора решает, какие объекты ещё достижимы, а аллокатор решает, вернутся ли освободившиеся страницы операционной системе. Освобождённый объект просто уходит обратно в кучу процесса, и пока в той же странице памяти лежит хоть один живой объект, отдать её ядру нельзя. У glibc malloc к этому добавляются кэш освобождённых блоков tcache и отдельные арены на каждый поток, так что многопоточный сервис держит несколько независимых запасов. В одном из примеров автора живые объекты занимали около 700 МБ при RSS около 2,8 ГБ.
🔘 признак, отличающий это от настоящей утечки: tracemalloc показывает стабильный объём, а RSS продолжает расти;
🔘 лечение обошлось без единой правки в коде Python, jemalloc подключается через LD_PRELOAD;
🔘 в продакшене к нему добавили PYTHONMALLOC=malloc и MALLOC_CONF=narenas:2,background_thread:true;
🔘 автор сообщает примерно о вдвое меньшем потреблении оперативной памяти после перехода;
🔘 проверить гипотезу можно и без замены аллокатора, через MALLOC_ARENA_MAX=2 и вызов malloc_trim(0).
Оговорка автора: malloc не универсально лучше встроенного pymalloc, на миллионах мелких объектов выигрыш может оказаться обратным, поэтому замену нужно мерить на своей нагрузке.
Приходилось ловить рост RSS при стабильном числе объектов, и чем в итоге объяснялось?
@zen_of_pythonnn.LinearCrossEntropyLoss объединяет последний линейный слой и функцию потерь, сокращая пиковый расход GPU-памяти до четырёх раз на моделях с большим словарём;
🔘 FlexAttention заработал на Apple Silicon;
🔘 torch.load() открывает safetensors без отдельной библиотеки;
🔘 появились Linux wheels для предварительных Python 3.15 и свободнопоточного 3.15t.
Заявленное ускорение FlexAttention примерно в 12 раз получено на конкретном разреженном шаблоне. Для плотного внимания SDPA остаётся быстрее. Сборки для Python 3.15 пока лежат в отдельном индексе PyTorch, а torch.compile с этой версией языка ещё не работает.
@zen_of_python--add-ignore. Он добавляет ruff:ignore к выбранной строке и отключает правило только в этом месте.
Новое preview-правило UP051 находит устаревшие декораторы из abc и предлагает замену. Команда ruff format получила --extend-exclude для дополнительных исключений. Проверка Jupyter Notebook теперь ловит синтаксические ошибки отдельно в каждой ячейке. В релиз также вошли оптимизации и исправления автофиксов.
@zen_of_python