19037
Полный Дзен Пайтона в одном канале Разместить рекламу: @tproger_sales_bot Правила общения: https://tprg.ru/rules Другие каналы: @tproger_channels Сайт: https://tprg.ru/site Регистрация в перечне РКН: https://tprg.ru/xZOL
Как __getitem__ превращает объект Python в итерируемый
Для цикла for объекту не всегда нужен __iter__. Если класс принимает целые индексы от 0, Python последовательно запрашивает элементы с индексами 0, 1, 2 и так далее. Получив IndexError, интерпретатор считает последовательность законченной.
В примере из статьи арифметическая последовательность вычисляет элемент по формуле прямо в __getitem__: seq[3] возвращает 14, а тот же объект без дополнительного метода работает в цикле.
Проверять поддержку целочисленных индексов заранее Python не станет. Поэтому обёртка над словарём выдаёт значения для ключей 0 и 1, а на ключе 2 падает с KeyError: эта ошибка не означает конец итерации. Механику и код можно посмотреть в статье Indexable iterables.
Pyodide разрешил публиковать WASM-колёса прямо в PyPI
В Pyodide 314.0 появилась прямая публикация пакетов для WebAssembly в PyPI. Раньше команда сама собирала и размещала пакеты, а теперь сопровождающие могут выпускать колёса для Pyodide так же, как для Linux, macOS и Windows.
На момент публикации в Simon Willison’s Weblog новые теги pyemscripten использовали 28 пакетов.
Почему в Python стоит подменять свой интерфейс, а не httpx
Если бизнес-тест напрямую подменяет httpx.Client и httpx.Response, даже проверка пустого списка требует трёх вложенных моков. Тест повторяет чужой API, а его смысл тонет в настройке объектов.
Автор предлагает поставить между бизнес-логикой и httpx тонкий DockerRegistryClient с методами get_repos() и get_repo_tags(). Бизнес-код работает с понятными операциями, тест подменяет один собственный объект, а смена HTTP-библиотеки не затрагивает эти проверки. Вложенность отступает перед явным интерфейсом: вполне по-питоновски.
В статье «Don’t Mock What You Don’t Own» in 5 Minutes есть код до и после рефакторинга и границы эвристики. Обёртка не нужна, если сторонний API уже удобен; для редких сетевых ошибок прямой мок тоже может оказаться проще.
Как точечно сравнивать скорость функций Python с tprof
Профилировщик помогает найти медленную функцию. Но после каждой правки снова измерять всю программу неудобно: отчёт приходится фильтровать, а профилировщик добавляет накладные расходы. tprof для Python 3.12+ следит только за выбранными функциями.
В режиме сравнения одна функция становится базовой, а столбец delta показывает разницу. В примере sum(range(...)) выполнялся примерно на 62% быстрее цикла с накоплением. Запустить замер можно из командной строки, через контекстный менеджер или декоратор.
В разборе Адам Джонсон показывает команды и Python API. tprof работает через sys.monitoring: регистрирует обработчики только для целевых функций, а время измеряет в C. Остальная программа не получает накладных расходов профилировщика.
Как упаковать Go-бинарник в wheel и подключить к Python
PyPI может раздавать не только Python-код. go-to-wheel собирает Go-программу в отдельные wheel-пакеты для Windows, macOS и Linux, а pip или uv выбирают сборку под ОС и архитектуру. Внутри пакета лежит бинарник; Python-точка входа находит его и запускает с переданными аргументами.
Simon Willison показывает схему на sqlite-scanner: утилиту можно вызвать через uvx sqlite-scanner, а другой Python-пакет может объявить её зависимостью и запускать через subprocess. Пользователю не нужны Go и ручная сборка. PyPI немного выходит за должностную инструкцию, зато граница остаётся явной: Go отвечает за бинарник, Python — за установку и запуск.
В статье Simon Willison’s Weblog разобраны структура wheel, команда сборки и пример плагина Datasette.
Что ускоряет django-msgspec в Django и где он расходится с json
django-msgspec заменяет компоненты Django и Django REST Framework аналогами на базе msgspec. Его написанные на C кодировщик и декодировщик JSON работают в несколько раз быстрее стандартной библиотеки. В пакете есть JsonResponse, тестовый клиент и json_script, а сериализаторы сессий и парсеры Django REST Framework подключаются через настройки.
Совместимость близка к json, но не полна. Числовые ключи словаря превращаются в строки, большие целые сохраняются. При этом бесконечность кодируется как null, а ключи None и bool вызывают ошибку. Explicit is better than implicit: перед заменой стоит прогнать тесты на реальных данных.
В разборе Адама Джонсона есть примеры настройки для ответов, сессий, dumpdata и loaddata, а также Django REST Framework.
Как перевести Python-сервер MCP с FastMCP на SDK 2.x
Свежая установка зависимостей сломала сервер автора: в requirements.txt пакет mcp был без ограничения версии, поэтому pip подтянул ветку 2.x, где FastMCP переименовали в MCPServer. Код репозитория не менялся, а прежний импорт перестал работать. Explicit is better than implicit, особенно в файле зависимостей.
Миграция начинается с замены импорта и имени класса. Декораторы @mcp.tool(), @mcp.resource() и тела инструментов в этом проекте остались прежними. Затем стоит закрепить mcp>=2,<3 и отдельно оставить httpx: SDK 2.x больше не устанавливает его как зависимость.
В пошаговом разборе на DEV Community есть проверки синхронных обработчиков, переменных окружения и позиционных аргументов конструктора. Так пакет mcp обновляется внутри репозитория и не откатывается для остальных проектов в системном Python.
Где заканчивается ускорение NumPy и что выбрать дальше
Векторизация выполняет цикл низкоуровневым кодом. В тесте прибавление 17 к 100 млн элементов заняло 6,13 секунды для списка CPython и 0,07 секунды для массива NumPy.
Предел виден, когда нужной операции нет в NumPy или вычисление создаёт промежуточный массив. np.abs(arr).mean() для массива на 1 ГБ выделяет ещё 1 ГБ под абсолютные значения. Цикл Python убирает копию, но возвращает расходы интерпретатора.
В разборе Python⇒Speed решения разделены по задаче. PyPy ускоряет циклы по обычным объектам, но плохо сочетается с NumPy. Numba компилирует функции для NumPy. Cython, Rust, C и C++ позволяют собрать расширение заранее.
«Явное лучше неявного» работает и для оптимизации: выбирайте инструмент по форме узкого места.
Как получать стабильный хеш Python-объектов
Встроенный hash() для этого не подходит: хеши строк и bytes меняются между процессами, а списки, словари и изменяемые dataclass он не принимает. hashlib детерминирован, но ждёт байты. Значит, сначала объект надо привести к однозначному набору байтов.
Автор разбирает, почему pickle не обещает одинаковый поток байтов, а str и repr могут терять значимые данные или включать адрес объекта. Решение строится на JSON: встроенные типы, datetime и dataclass приводятся к поддерживаемому представлению, неподдерживаемые значения вызывают ошибку, пустые и выбранные поля можно пропустить.
В статье есть требования, промежуточные неудачные варианты и итоговый код без сторонних зависимостей. Подход пригодится, если хеш хранится дольше одного процесса или должен совпадать в разных реализациях Python.
Космический хакатон. Санкт-Петербург, Красноярск или онлайн из любой точки страны.
Старт 18 сентября.
Для разных городов разные задачи, а за выходные участники должны представить рабочие решения.
Санкт-Петербург. К 2035 году над Землёй должен работать топливный узел, у которого заправляются корабли, и откуда он будет получать топливо — с Земли, с Луны или из резерва — пока не решил никто. Команде предстоит собрать схему поставок, посчитать резервы и решить, во что вкладываться сразу. Призовой фонд 1,2 млн ₽.
Красноярск. Спутник видит горящий лес раньше любого наземного поста, но вместе с пожаром ловит нагретые крыши, факелы на месторождениях и блики на воде. Команде предстоит научить сервис отличать настоящий очаг от шума, обвести гарь и выдать площадь в гектарах. Призовой фонд 600 тыс. ₽.
Лучшие забирают приз на площадке и билет в финал в Москве, 25–27 сентября, где разыграют ещё 2,4 млн рублей.
Команда 3–5 человек, недостающих можно найти на платформе.
Регистрация по ссылке — космохакатон.рф
Как устроен асинхронный веб-краулер на Python
Обстоятельная глава строит краулер с нуля: от корневого URL он скачивает страницы, извлекает новые ссылки и ставит их в очередь. Число одновременных запросов ограничивают, чтобы избыток конкуренции не снижал производительность.
Разбор идёт от цикла событий с неблокирующими сокетами и колбэками к корутинам на генераторах, а затем к asyncio с асинхронной очередью. Переходы показывают, как избежать неуправляемых цепочек колбэков.
Асинхронность не означает параллельные вычисления и не обязана быть быстрее потоков. Она подходит для множества медленных соединений с редкими событиями, где поток на каждый запрос расходует память и упирается в системные ограничения.
В главе «A Web Crawler With asyncio Coroutines» из 500 Lines or Less код написан для Python 3.4. Читайте ради механики цикла событий; перед копированием сверяйте API с документацией.
Наследование, композиция или функции: сравниваем на Executor в Python
Статья сравнивает три реализации гибридного исполнителя задач: наследование, композицию и функции. Он разносит работу по процессам, а внутри каждого запускает потоки, чтобы I/O-нагрузка использовала все ядра, когда упирается в CPU.
Наследник ProcessPoolExecutor переопределяет __init__, submit() и shutdown(), а map() и контекстный менеджер получает от родителя. При композиции map() приходится копировать для вызова нового submit(), а контекстный протокол писать заново. «Явное лучше неявного» внезапно означает больше кода.
В разборе решения сравниваются по совместимости с будущими версиями, глобальному состоянию, сложности и отладке. Для полноценной замены считайте не только связи между классами, но и методы, которые придётся делегировать вручную.
Как сделать параметризованные тесты pytest читаемыми
Обычный список кортежей в @pytest.mark.parametrize быстро усложняет чтение: значения приходится сопоставлять с аргументами по позиции, а идентификаторы сценариев хранятся в отдельном списке.pytest.param помещает id рядом с данными. Тогда отдельный сценарий можно запустить командой pytest -k positive_x_axis. Через marks там же задаются xfail для ожидаемого сбоя и skipif для условного пропуска. Явное лучше неявного, особенно когда тест падает выборочно.
В статье Taming parametrize with pytest.param показан и следующий шаг: собрать именованные аргументы и ожидаемый результат в одном словаре. Такой вариант стоит примерить на тестах с длинной сигнатурой: у каждого сценария данные, имя и условия запуска оказываются рядом.
Как собрать LRU-кеш с приоритетами и сроком жизни на Python
Обычный LRU-кеш удаляет давно не использовавшиеся записи. Здесь порядок сложнее: сначала просроченные элементы, потом записи с меньшим приоритетом, при равенстве — давно не запрашиваемые.
Автор начинает со словаря с операциями в среднем за O(1) и добавляет структуры для срока жизни, приоритета и истории обращений. Наивная очередь хранится в отсортированном списке: минимум легко прочитать, но вставка и удаление с начала требуют линейного времени. Затем очередь ускоряют через bisect, без куч и деревьев.
Разбор Адриана на death and gravity показывает, как согласовать несколько структур данных и проверить сроки через внедряемые часы. Это пример разработки от простого рабочего варианта к более быстрому только на стандартной библиотеке.
Как тестировать HTTP-запросы в Python без хрупких моков
У асинхронного запроса через HTTPx есть четыре способа проверки. Можно вручную подменить AsyncClient.post, настроить асинхронный мок и проверить аргументы. С усложнением запроса растёт и эта обвязка.
Библиотека respx перехватывает запросы HTTPx и возвращает заготовленный ответ. Код короче, зато тест остаётся привязан к HTTPx. Более явный вариант в духе Python: передать клиент в функцию и на тесте заменить его объектом-заглушкой.
Для интеграционной проверки приложение Starlette играет роль тестового сервера, а AsyncClient обращается к нему вместо внешней сети. В статье есть код всех четырёх вариантов. Выбирайте respx для компактной подмены, заглушку вместо сторонней библиотеки для моков, тестовый сервер для проверки связки целиком.
Что меняет re.prefixmatch() в Python 3.15re.match() проверяет совпадение только в начале строки. Поэтому шаблон из шести цифр принимает и 345071-in-abbey-wood: подходящий префикс найден, а остаток не мешает успеху. Для проверки всей строки по-прежнему нужен fullmatch().
В Python 3.15 появится prefixmatch(), точный синоним match(). Поведение не изменится, зато имя прямо сообщит, что проверяется лишь начало строки. Явное лучше неявного, даже когда речь всего лишь о названии метода.match() получит статус мягко устаревшего: предупреждений и планов удаления нет, существующий код продолжит работать. В новом коде под Python 3.15+ стоит выбирать prefixmatch() для проверки префикса, а fullmatch() для валидации значения целиком. Примеры и объяснение различий собраны в разборе Адама Джонсона.
Как применять структурное сопоставление с образцом к деревьям в Python
Структурное сопоставление особенно выразительно на рекурсивных структурах. Автор моделирует булевы выражения датаклассами Var, And, Or и Not, а затем разбирает каждый тип веткой case.
Вычислитель захватывает поля прямо в образце. Для переменной он берёт значение из словаря, для And и Or рекурсивно обходит дочерние выражения через all() и any(), для Not инвертирует результат. Общая ветка выбрасывает ошибку, если появился неизвестный подкласс: явное лучше неявного.
Тот же приём автор переносит на форматированный вывод дерева. Получается компактный разбор того, как рекурсивно применять match к древовидным данным, не превращая обход в цепочку проверок типов.
Как точно типизировать *args и **kwargs в Python
Запись *args: tuple[int, str] выглядит как тип всего кортежа, но проверщик читает её иначе: каждый позиционный аргумент должен быть таким кортежем. У **kwargs: dict[...] тип также относится к каждому именованному аргументу. Типизатор здесь не телепат.
Для разнородных аргументов нужен Unpack: Unpack[tuple[int, str]] задаёт типы и порядок позиционных аргументов. Именованные описываются через TypedDict и Unpack[Kw], поэтому проверщик знает тип каждого ключа.
В разборе аннотаций показаны и необязательные ключи: все сразу через total=False либо отдельные через NotRequired. До Python 3.12 эти типы берутся из typing_extensions. Подход пригодится обёрткам, которые передают аргументы функции с той же сигнатурой.
Как писать Python-скрипты, которые удобно запускать в пайплайнах
В разборе Bite code! собраны приёмы, которые оставляют однофайловый скрипт простым, но делают его предсказуемым для пользователя.
Зависимости указываются в комментарии в начале файла: uv run создаёт временное виртуальное окружение, ставит пакеты и запускает скрипт. Секрет сначала ищется в переменной окружения с префиксом имени скрипта, затем в хранилище ключей ОС, а при отсутствии запрашивается через getpass и сохраняется после проверки.
Результат операции отправляйте в stdout, ошибки и журнал в stderr: тогда конвейер получит только данные. Для пользователя оставляйте print(), диагностику включайте через logging по запросу, а переменные окружения перечисляйте в --help через argparse.
Как collections.deque хранит элементы блоками
У deque два конца, поэтому легко представить узел на каждый объект. В CPython звено списка хранит блок до 64 элементов. Соседние блоки связаны в обе стороны, а индексы указывают на первый и последний элементы.
При append и pop внутри блока меняются индекс и ячейка. Новый блок нужен, лишь когда крайний заполнен; опустевший крайний блок отсоединяется. Поэтому интерпретатор реже выделяет и освобождает память, чем при отдельном узле на элемент, и deque работает быстрее.
В разборе на mathspp.com эта механика собрана в упрощённой Python-реализации для Python 3.15. По коду можно проследить append и pop; левый край устроен симметрично.
Почему миллион чисел в Python занимает 35 МБ
Список из миллиона целых, которые помещаются в 64 бита, занимает в CPython около 35 МБ вместо ожидаемых 8 МБ. Из них 8 МБ уходят на указатели списка, ещё 28 МБ на числа: небольшой объект int весит 28 байт.
В CPython каждое число является объектом. Кроме значения, у него есть счётчик ссылок и указатель на тип. На 64-битной системе эти служебные поля добавляют минимум 16 байт к каждому объекту. Явное лучше неявного, особенно в профиле памяти.
Массив NumPy с типом uint64 хранит значения вместо ссылок на отдельные объекты Python. Поэтому миллион элементов занимает 8 МБ плюс память на импорт библиотеки. В разборе структуры объектов CPython есть замеры и код. Для больших числовых наборов проверьте NumPy до того, как 8 ГБ превратятся в 35 ГБ.
Как кэшировать методы чужого Python-клиента
Если библиотечный клиент нельзя менять, его методы можно переопределить с functools.lru_cache. Но кеш тогда включает self в ключ и удерживает экземпляр до вытеснения записи или очистки.
Питоничнее обернуть уже связанный метод после создания объекта: кеш будет отдельным для каждого экземпляра, поэтому состояния клиентов не смешаются. Для множества методов автор перехватывает обращения через __getattribute__, кеширует метод при первом вызове и сохраняет обёртку в __dict__. Если объект создаёт фреймворк, эту механику можно вынести в прокси.
В разборе Caching a lot of methods in Python показано, как вернуть прокси автодополнение через __dir__ и проверки isinstance() через wrapt.ObjectProxy. Для пары методов решение проще: заменить их кешированными прямо у готового объекта.
Как подключать pytest-фикстуры без лишних аргументов теста
Иногда фикстура нужна только ради побочного эффекта: например, перед тестом она подменяет os.environ. Если добавить её в параметры функции, pytest всё выполнит, но IDE подсветит неиспользуемый аргумент. Автор избегает autouse=True: по его оценке, так о тестах труднее рассуждать.
Декоратор @pytest.mark.usefixtures с именем mock_env назначает фикстуру конкретному тесту. Pytest запускает mock_env, а в сигнатуре остаются только объекты, к которым код обращается напрямую. Декоратор принимает несколько имён фикстур, но помечать им другую фикстуру нельзя.
В короткой статье с примером показано, как убрать серый неиспользуемый параметр и сохранить зависимость на виду. Явность победила, сигнатура тоже.
Когда namedtuple всё ещё лучше dataclass
После появления dataclass именованный кортеж перестал быть универсальной заменой маленькому классу. Он всегда поддерживает индексацию и распаковку, поэтому новое поле ломает код с распаковкой, а публичный API рискует навсегда сохранить кортежный протокол.
Это же поведение полезно, когда данные естественно упорядочены: строки БД, результаты разбора бинарного формата, пары заголовков HTTP. Если API уже возвращает обычный кортеж, namedtuple добавит имена полей, не сломав прежних потребителей. Можно и намеренно распаковывать все поля: забытый обработчик упадёт сразу после изменения структуры. Явное лучше неявного, как и договаривались.
В разборе namedtuple и dataclass автор также сравнивает память и скорость. На миллионах экземпляров разница может стать заметной, а в большинстве программ она несущественна.
Как изолировать Pydantic Settings от окружения в pytest
Если Settings создаётся при импорте пакета, он читает .env и переменные оболочки до запуска фикстуры pytest. Подмена os.environ запаздывает: значения уже сохранены в объекте, а тест зависит от окружения.
Решение автора: фикстура сохраняет model_copy() объекта, сбрасывает поля к значениям по умолчанию из model_fields и применяет точечные переопределения. Неизвестные настройки и неверные типы она отклоняет, после yield восстанавливает исходное состояние.
Подход пригодится, когда Settings уже живёт на уровне модуля. Явное снова лучше неявного. Код фикстуры и тесты со значениями по умолчанию и частичными переопределениями смотрите в разборе подмены Pydantic Settings.
Почему mock.patch не подменяет функцию в Python
После from os import listdir имя listdir привязано в модуле product. Последующая подмена os.listdir эту привязку не меняет, поэтому код продолжает вызывать product.listdir.mock.patch работает как временное присваивание: заменяет имя по указанному пути, а после блока with возвращает исходное значение.
Практическое правило: подменяйте имя там, где его ищет тестируемый код. Здесь нужен mock.patch("product.listdir"). Он изменит поведение только product.py, не затрагивая другие модули. В статье Why your mock doesn’t work механизм разобран на двух способах импорта и схемах связей между именами.
Как быстрее загружать грязные данные в PostgreSQL из Python
Автор берёт постраничный API с данными о пиве, превращает его в генератор и перед записью нормализует поля: извлекает объём из вложенного объекта, а месяц и год приводит к дате. Для замеров 325 записей дублируются 100 раз, получается набор из 32 500 строк.
Затем импорт сравнивается через построчные INSERT, executemany, execute_batch, execute_values и COPY. Для пакетных вариантов с итераторами меняется размер страницы, для COPY из строкового итератора — размер буфера. У каждого подхода измеряются время и пиковая память. Явное лучше неявного, особенно когда скорость не приходится угадывать.
В подробном разборе есть код генератора, преобразований, профилировщика и всех вариантов загрузки. Его удобно использовать как основу замера на реальных данных.
Как ждать корутины в asyncio
Два последовательных await не дают конкурентности: вторая корутина запустится после первой. Создание двух объектов корутин тоже не планирует их выполнение. Для этого нужны задачи, созданные до первого ожидания.
У каждого способа свои гарантии. gather() возвращает результаты в порядке аргументов. Если одна задача выбрасывает исключение, оно передаётся вызывающему коду сразу, а остальные задачи продолжают работать. as_completed() отдаёт результаты по мере готовности. wait() делит задачи на завершённые и ожидающие, а его тайм-аут сам ничего не отменяет.
В статье Waiting in asyncio автор советует начинать с TaskGroup, а для более гибкого управления выбирать wait(). Здесь «явное лучше неявного» работает буквально.
Есть одна странная вещь в AI-индустрии.
Сегодня лучшая модель для кода — одна. Завтра выходит другая. Через месяц рынок снова меняется. Но большинство разработчиков всё ещё строят инфраструктуру так, будто им нужно выбрать одного AI-провайдера навсегда.
Polza.ai работает наоборот. Вы подключаете один API и получаете доступ к сотням моделей — OpenAI, Claude, Gemini, DeepSeek, Qwen и другим.
Нужна другая модель? Меняете её в запросе — и продолжаете работать. Можно сравнить провайдеров и цены и выбрать оптимальный вариант под конкретную задачу. А если один провайдер перестал отвечать, можно включить автомаршрутизацию — запрос автоматически уйдёт к другому.
Один баланс, одна интеграция, оплата в рублях. А в кабинете прозрачно видно всё по расходам: модель, провайдер, стоимость, количество токенов и статус каждого запроса. Расходы можно отслеживать в реальном времени.
В итоге вам не нужно угадывать, какая модель победит. Вы сами выбираете, что лучше подходит для конкретной задачи, и не привязываетесь к одному провайдеру.
Попробовать Polza.ai.
Как ускорить сериализацию в Django Rest Framework
В бенчмарке на Python 3.7, Django 2.1.1 и DRF 3.9.4 объект User сериализовали 5000 раз без обращений к БД. ModelSerializer потратил 12,8 с, а функция справилась за 0,034 с. Разница составила 377 раз.
Профилировщик показал цену автоматизации: ModelSerializer заново строил поля из метаданных модели и готовил проверки. Режим только для чтения сократил время до 7,4 с, а Serializer с явно объявленными полями уложился в 2,1 с. Явное оказалось не только лучше, но и быстрее.
Для нагруженного API отделите время запроса к БД от сериализации. Если тормозит последняя, сделайте выходные поля доступными только для чтения или замените ModelSerializer на Serializer; для предельной скорости сравните с обычной функцией. Код и профили cProfile есть в разборе производительности DRF.