Резюме

  • Появление долгожданных фич из нового REPL в PDB

  • Большое количество фиксов для shutil, так что можно наконец‑то перестать молиться при его использовании

  • Несколько небольших улучшений многопоточности

  • Новый синтаксис аннотаций позволяет использовать списки и лямбда‑функции.

  • Python 3.13 все еще не стал значительно быстрее. Увы.

По традиции

Python 3.13 — отличный релиз, полный различных фич и улучшений, но уже есть тонна статей, которые подробно разбирают release notes. Если вам нужна хорошая выжимка — у RealPython есть хорошая статья, но я не вижу смысла проходиться по ним еще раз в этой статье.

Так что мы не будем говорить про новый REPL, no‑GIL сборку, экспериментальный JIT‑компилятор, устаревшие штуки, новые плюшки системы типов или улучшенные сообщения об ошибках (как всегда, мое любимое).

Вместо этого я прочитал коротенькую книжку, которую они называют ченджлогом и мы посмотрим на то, о чем многие не говорили, но заинтересовало лично меня.

Make debugging great again

Несмотря на спартанскую эргономику, я очень люблю pdb и даже написал неплохое введение в него.

Но если вы когда‑либо пробовали сделать так:

try:
    1 / 0
except ZeroDivisionError as e:
    breakpoint()

Вы прекрасно знаете, что произойдет при попытке чтения e:

-> breakpoint()
(Pdb) e
*** NameError: name 'e' is not defined

Это выбешивает, в особенности потому, что с этим обычно сталкиваешься и так в не самом лучшем расположении духа.

И это наконец‑то исправили.

Слава Апофису, Кетцалькоатлю и Йормунганду!

Но это не все. Сам pdb также получил некие улучшения:

Все это само по себе уже хорошая причина использовать 3.13.

Немного любви для работы с файловой системой

Казалось бы, 30-летний язык не должен иметь проблем при работе с путями и файлами, но, оказывается, всегда есть куда расти.

Модуль shutil, предоставляющий высокоуровневые операции с ФС, такие как рекурсивное удаление или копирование претерпел следующие изменения: многомного багов было исправлено (в особенности относящиеся к обработке ошибок при рекурсии), а также добавлены некоторые опции (например, теперь есть возможность выбрать, как обрабатывать симлинки).

Я всегда опасался использовать shutil из‑за таких «особенностей». Рекурсивное погружение ломалось тем или иным образом. Благодаря этим фиксам я смогу снова попробовать использовать ее, поскольку она действительно удобна, когда работает.

Аналогично и для zipfile.Path — pathlib‑совместимой обертки для обхода zip‑файлов, о существовании которой вы скорее всего не знали с момента ее появления в версии 3.8. И не знали вы о ней по очень простой причине — в ней не было ничего хорошего. Но версия 3.13 подарила нам много QoL‑патчей для нее, значительно улучшая обработку директорий, которая раньше требовало очень много телодвижений. Так что теперь ее можно будет использовать чаще.

И, наконец, сама pathlib получила много небольших оптимизаций, касающихся производительности, теперь многие операции под капотом используют строки вместо объектов Path. Сериализация также должна стать быстрее. Мне нравится pathlib, но возможность боттлнека — известная проблема, так что это отличные новости. Сам я производительность на практике не замерял, так что пока воздержусь от комментариев.

Но дело не только в производительности - также было несколько приятных изменений в API. К примеру, Path.glob() и rglob() теперь принимают в качестве паттернов path-like объекты, Path.glob() теперь возвращает и файлы, и директории, если в конец паттерна добавить **, а также был добавлен метод Path.from_uri().

Небольшие победы в параллелизме

Изменения аннотаций, о которых никто не просил

Помните, что аннотации изначально не были ограничены только типами? Это был эксперимент, чтобы проверить, для чего их будут использовать люди и потому они принимают произвольные выражения Python.

Но, похоже, недостаточно произвольные, поскольку была добавлена возможность использования списков и лямбда‑функций.

Тикет с баг‑репортом достаточно забавный:

The following code causes a SystemError during compilation.

class name_2[*name_5, name_3: int]:
    (name_3 := name_4)

    class name_4[name_5: name_5]((name_4 for name_5 in name_0 if name_3), name_2 if name_3 else name_0):
        pass

Да что ты говоришь, Фрэнк, да что ты говоришь…

Что ж, посмотрим, какие еще способы поиздеваться над этим функционалом придумают разработчики.

Разочарования

Улучшения производительности не такие высокие, как ожидалось в этом релизе — как и в 3.12 мы получили лишь небольшое улучшение в традиционных бенчмарках для асинхронных операций (надеюсь, это не навредит моему оптимизму относительно pathlilb). Ускорение Python гораздо, гораздо сложнее, чем Guido предполагал. Это можно было понять из прошлых неудавшихся попыток, так что не особо удивительно.

Как я упоминал в прошлом посте, инкрементальные изменения GC и правда были отменены, поскольку не дали ожидаемых результатов.

Более того, несмотря на большое количество улучшений import в ченджлоге, мои замеры не показали значительной разницы во времени запуска Python на моей машине.

Ну и поверх всего этого неизбежные поломки:

Если вы внимательно прочитаете изменения Path.glob() выше, вы можете заметить, что возвращаемое значение может значительно меняться и я предполагаю, что это сломает чей‑то код.

Ну и несколько других деталей, которые могут не понравиться некоторым:

  • Поддержка использования pathlib. Объекты path больше не могут использоваться для управления контекстом. Не то чтобы это было полезно. И вообще это путало. Так что, наверное, хорошее изменение, но вы знаете.

  • Запуск новых тредов и процессов с помощью os.fork() во время выключения интерпретатора (например, из хэндлеров atexit) больше не поддерживается.

  • Позиционные аргументы maxsplit, count и flags для re.split(), re.sub() и re.subn() помечены как устаревшие в пользу использования ключевых слов. Пока не убрано, просто предупреждение. Мне кажется несколько излишним.

  • Аналогично для всех аргументов sqlite3.connect(), кроме первого.

  • Устаревание незадокументированных функций glob.glob0() и glob.glob1(). Я терпеть их не мог, так как они путали моих студентов, так что я рад этому изменению, но все же.

  • Файлы .pth с названиями, начинающимися с точки или имеющие атрибут hidden теперь игнорируются по причинам безопасности.

  • Хэдеры C API были почищены, так что ждем драконов с той стороны.

Разработка языка это всегда тонкий баланс между чистотой кода, современными фичами и отсутствием технического долга с одной стороны и поддержанием достаточной стабильности для сохранения продуктивности сообщества с другой.

Всегда найдутся как люди, которые будут говорить, что язык затрял в прошлом, так и те, кто будут спрашивать, почему опять все сломали. Здесь не победить. Разработка языка — работа неблагодарная.

Разные приятные изменения

Давайте закончим на позитивной ноте. У меня нет какой-либо категории для этих изменений:

Что удивило меня больше всего при прочтении ченджлога версий 3.12 и 3.13 - фокус на улучшении уже существующего функционала. Большое количество багфиксов, небольших правок API, попытки немного улучшить производительность, чистка кода и удаление устаревшего. Даже известная плохая документация argparse стала лучше.

Спустя годы преследования новых фич с асинхронностью, типизацией, моржовым оператором и несколькими интерпретаторами, я ожидал от новых людей в команде разработки лишь увеличения этой тенденции. Но вместо этого мы получили улучшения сообщений об ошибках, улучшенный PDB, более гибкий парсер, улучшенный REPL и так далее.

И для меня это знак, что им не все равно.

Комментарии (2)


  1. ars_zero_red
    05.11.2024 17:02

    А для чего еще используются аннотации?


  1. Jury_78
    05.11.2024 17:02

    : много-много багов было исправлено... , а также добавлены некоторые ...

    Так получается, если читать бегло... :)

    «Заберите у товарища брак и выдайте ему новый!» (фильм «Волга-Волга»)