Февральскую конференцию EkbPy в Екатеринбурге открывал Андрей Гейн со своим субъективным обзором главных новостей Python. Этот доклад стал одним из лучших на конференции по мнению слушателей, и мы решили поделиться некоторыми его тезисами с вами. 

Новости из мира Python

Начнем с общего. Мало кто знает, но Python впервые целый год каждый месяц занимал первую строчку в рейтинге языков программирования TIOBE. Угадайте, какому языку TIOBE отдал премию «Язык года 2022»? Правильно, C++. Мир несправедлив ????. 

Вторая важная новость — ребята запустили сайт https://peps.python.org.

Это сайт, на котором теперь собираются и каталогизируются PEP-ы — предложения по улучшению питона (раньше они жили на https://www.python.org/dev/peps/ ). В описаниях PEP-ов можно найти кучу интересных штук, которых вы потом не найдете в документации или статьях — например, «как мы хотели сделать, но нас отговорили» или «какие мы рассматривали варианты, но поняли, что они плохие». Горячо рекомендую принятые или обсуждаемые PEP-ы к воскресному чтению!

Python 3.11 

Релиз нового питона вышел уже не вчера, и многие про него что-нибудь да слышали. Но в релиз вошло так много интересного, что мимо, конечно, не пройти. Кстати, процесс релиза транслировался на YouTube.

Релизная команда собралась на видео-встречу, надела смешные шляпы и три часа релизила Python. Во время релиза они травили байки и читали мини-доклады про то, что вошло в этот релиз и как они над ним работали. Это очень интересное и познавательное видео, которое можно включать перед сном и смотреть частями. Всем рекомендую!

Какие ещё общие новости про релиз? Наконец-то объявлена устаревшей lib2to3 — библиотека, отвечающая за перевод кода из второго питона в третий. Теперь не гарантируется, что эта библиотека (и соответствующая ей утилита 2to3) будут корректно парсить код, написанный на питоне версии 3.11+. Почему я считаю, что это важная новость? Мы давно слышим разговоры о том, что второй питон умер, но то там, то тут появляются новости, кто-то и где-то его ещё поддерживает. Кажется, теперь эти хвосты отрезаются окончательно.

Ну а главные изменения версии 3.11 связаны со скоростью работы питона. Наверняка, все из вас хотя бы раз слышали, что Python — жутко тормозной язык. За тормоза хейтят язык, а нас с вами иногда хейтят за то, что мы его любим ????. Поэтому очень важна та работа, которую ребята проделали в районе оптимизаций скорости работы программ на питоне. В конце 2020 года Марк Шеннон и Гвидо ван Россум запустили проект Faster CPython. Он нацелен на ускорение CPython, основного интерпретатора питона, и по их плану за четыре релиза (и, соответственно, четыре года) Python ускорится в пять раз. Велика вероятность, разумеется, что такого значительного результата добиться не получится, но ставить амбициозные цели перед собой — абсолютно правильно. 

Что получилось оптимизировать в Python 3.11?

Разработчики внедрили в интерпретатор четыре так называемых тира исполнения (Tiers of Execution). Подразумевается, что программа работает медленно, потому что некоторые участки кода выполняются очень много раз. Ребята начали применять самые жёсткие оптимизации именно к тем участкам кода, которые выполняются наиболее часто. Соответственно, как только какой-то участок кода начинает исполняться достаточно часто, его переводят в следующий тир, где применяются более агрессивные оптимизации. Для редко же используемого кода интерпретатор не делает практически никаких оптимизации, чтобы не тратить ни дополнительную память, ни дополнительное время. 

Далее, в интерпретаторе реализовали PEP 659: Specializing Adaptive Interpreter. О чём это? Как известно CPython превращает программу в некоторый набор инструкций — байт-код. Например, существует инструкция LOAD_ATTR, которая загружает на стек атрибут. Этот атрибут может быть как атрибутом модуля, так и атрибутом класса, объекта и так далее. Как следствие, внутри реализации инструкции LOAD_ATTR написан достаточно развесистый if.

Идея же адаптивного интерпретатора заключается в том, что если в часто-исполняющейся строчке кода инструкция LOAD_ATTR чаще всего загружает атрибут именно модуля, то скорее всего и в следующий раз атрибут нужно будет загрузить из модуля. В итоге, адаптивный интерпретатор в какой-то момент просто заменяет инструкцию LOAD_ATTR на LOAD_ATTR_MODULE. Естественно, в интерпретатор встроили защиту: если вдруг в очередной раз из-за слабой типизации питона мы увидим не модуль, а переменную, то выполнится честный LOAD_ATTR_INSTANCE_VALUE. Зато в большинстве случаев выполнение пойдет по более быстрому пути, что позволяет сэкономить 5-10 процентов времени!

Кроме того, уменьшили время старта самого интерпретатора СPython, что будет полезно, если вы часто запускаете маленькие скрипты. Раньше, когда интерпретатор запускался, он загружал стандартную библиотеку как обычный модуль: шёл в системный __pycache_, брал нужный .pyc-файл, выделял место в памяти, загружал туда инструкции и данные, и при необходимости передавал туда управление. Теперь этого всего просто нет. Теперь CPython сразу стартует со статически-выделенным местом под ядро языка и стандартную библиотеку. 

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

Короче говоря, в рамках проекта Faster CPython действительно неплохо ускорили питон. Если верить бенчмаркам авторов, то в среднем программы ускоряются на 10-30 процентов. Но как и всегда,  лучший способ узнать, помогут ли оптимизации лично вам, — это взять какой-нибудь ваш код, и посмотреть, насколько он ускорится с Python 3.11. 

Другие оптимизации

Как ни странно, работами в рамках проекта Faster CPython оптимизации не ограничиваются.

Например, в версии 3.11 реализовали следующую идею: давайте на лету заменять старое форматирование через оператор % в аналогичную f-строку. Зачем? Ну просто форматирование f-строки работает примерно в два раза быстрее, чем форматирование с процентом.

Еще ускорили деление целых чисел и вызов функции sum() для списков чисел, меньших 230, на x86-64. Целые числа и массивы из них встречаются достаточно часто в наших программах, поэтому такие оптимизации выглядят достаточно важным, но есть нюанс — оказывается, ускорение случилось не таким хорошим, как его разрекламировали в чейнджлоге, и ишью пришлось переоткрыть. 

Exception Groups

Нельзя пройти мимо и самого сложного за последние пять лет нововведения питона — групп исключений (Exception Groups). Это достаточно новаторская штука, и мало в каких языках есть подобное. Группы исключений позволяют выбросить сразу несколько исключений одномоментно, причем исключения образуют не список, а дерево, что позволяет задать сложную структуру отношений между исключениями.

Чтобы ловить группы исключений, авторы питона добавили целый новый оператор: except*. Подробно о работе групп исключений и новом операторе можно посмотреть в полной записи доклада Андрея

Вместе с возможностью выкинуть и поймать сразу несколько исключений появилась ещё одна фича, связанная с исключениями, — заметки (Exception notes). 

Поймав исключение, вы теперь можете добавить ему заметку, которая прокинется дальше по всей цепочке и будет залогирована вместе с исключением. Очень удобный способ добавлять контекста в сообщения об ошибках. 

Идём дальше. Как и в 3.10, в версии 3.11 улучшили формулировки ошибки. Я считаю, что это классная фича, потому что для многих питон является первым в жизни языком программирования, языком на котором учатся вообще программировать. Так, например, начали указывать конкретное место, где случилась ошибка.

Появился Variadic Generics. В мире аннотаций типов, большим любителем которой я являюсь, существенное нововведение: теперь можно объявлять собственные типы-дженерики. Два года этого ждал:

Разумеется, это чисто синтаксическая фича, никак не влияющая на рантайм. Но зато сколько новых возможностей нам открывается! Если вы тоже интересуетесь типизацией в питоне, посмотрите кусочек про Variadic Generics в записи доклада.

Ну и напоследок, несколько фишек 3.11 одним предложением

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

typing.LiteralString — тип, при использовании которого проверяется, что переменная — это не просто строка, а константная строка. 

Сильно прокачали enum: появились enum.StrEnum, @verify(enum.UNIQUE), enum.FlagBoundary и многое другое. 

Нельзя не рассказать и про пару PEP-ов, которые по разным причинам не попали в 3.11:

Что известно про 3.12

Если честно, про следующий релиз пока известно достаточно мало. Расскажу про два ожидаемых лично мной PEP-а, включение которых сейчас таргетировано на 3.12. 

PEP 679 Allow parenthese in assert statements должен позволить добавлять скобочки в assert.

Мелочь, а приятно.

И PEP 701 Syntactic formalization of f-strings. Меня всегда раздражает, что если я хочу внутри f-строки обратиться к элементу словаря, то должен обязательно использовать другой вид кавычек.

А если там что-то ещё более сложное, то я должен эти кавычки хитро чередовать и экранировать. Наконец-то это хотят починить и сделать так, чтобы внутри f-строки можно было писать любое корректное выражение!

Библиотеки

Было ли что-то новое в мире библиотек? Если честно, те чейнджлоги, что я посмотрел за последний год, меня не впечатлили. В Django, который никак не может умереть, выпустили релизы 4.0 и 4.1. Что там нового: 

  • Кеширование в редисе

  • У форм теперь можно указывать template_name

  • Функциональные констрейнты (но абсолютно такие же функциональные индексы уже были в 3.2)

  • Асинхронные хендлеры в классовых вьюшках (но и здесь ничего принципиального нового — асинхронные вьюшки уже были в 3.1)

  • Асинхронная ORM

В общем, не тянет как-то на «релиз года», простите.

Библиотека Black, которой большинство проектов доверяет форматирование своего исходного кода, выпустила релиз 22.1.0. Почему это вообще может быть важно? А важно это потому что начиная с этой версии библиотека выходит из беты. Да-да, если вы не знали, раньше никаких гарантий стабильности не было, и вот только теперь Black объявлен стабильным. При этом ребята полностью вырезали поддержку второго питона, поэтому повторюсь ещё раз: если у вас остался код на втором питоне, съезжайте как можно скорее! 

В релизах SqlAlchemy 2.0, IPython 8.0, FastAPI не произошло ничего такого, о чем хотелось бы рассказать. Еще случился PEP 594, который объявил часть стандартных библиотек устаревшими, что стандартный набор библиотек хорошо бы покоцать. В целом, правильное начинание: библиотеки, которые были придуманы в 1994 году и давно не поддерживаются, надо чистить.

Еще немного новостей

  1. Интересное исследование нашло несколько зловредных пакетов в PyPI, ворующих ваши креденшелы, разные важные файлы и настройки. Будьте аккуратны, не ставьте абы какие пакеты  

  2. Интересный проект, развития которого я очень жду, — это nogil. Ребята пытаются избавиться от того самого GIL, за который все хейтят питон после того, как перестают хейтить его за тормознутость. По сути, nogil — это форк CPython с попыткой отвязаться от GIL-а. На синтетических тестах многопоточные программы получают ускорение на 5-10%.

  3. Минутка безопасности. Ребята научились запускать os.system() благодаря проезду по памяти в CPython, не вызывая при этом, собственно, не вызывая os.system()

Ruff

Ещё я в каждом выпуске новостей рассказываю о сторонних проектах, которые меня заинтересовали. Обо всех проектах можно послушать в полной записи доклада, здесь же напишем о Ruff. Это линтер для питона, написанный на Rust, который рвёт по скорости все остальные линтеры:

Меня нельзя назвать большим любителем Rust, но я уверен, что именно так и надо использовать сильные стороны разных языков программирования. Если у вас есть быстрый Rust, то давайте писать на нем те части кода, в которых критично время. Давайте не хейтить языки друг друга, а искать точки соприкосновения и жить дружно ???? 

Это лишь часть выступления Андрея на конференции EkbPy, которая прошла в феврале в Екатеринбурге. Целиком его можно посмотреть на нашем YouTube-канале, как и десятки других крутых выступлений с наших конференций.

Присоединяйтесь к нашему сообществу! Ближайшая встреча питонистов пройдет 28–29 июля, на ежегодной конференции PyCon Russia 2023. Мы уже работаем над программой, подавайте заявки на доклады, бронируйте билеты и планируйте встречу. 

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


  1. Megadeth77
    00.00.0000 00:00
    +1

    Спасибо, познавательно.
    https://habrastorage.org/r/w1560/getpro/habr/upload_files/01a/21b/f36/01a21bf36206b6a31358b3980d311cfc.png
    А в двух словах чем это отличается от обычных дженериков в 3.10?

    import typing
    
    T = typing.TypeVar("T")
    
    
    class List(typing.Generic[T]):
        def __init__(self):
            self.data: list[T] = []
    
        def add(self, item: T):
            self.data.append(item)
    
        def pop(self) -> T:
            return self.data.pop()
    
    
    l = List[int]()
    # l.add("q") <- Expected type int 
    l.add(1)
    res = l.pop()  # res: int
    print(l.pop())
    
    


    1. Helltraitor
      00.00.0000 00:00
      +1

      https://peps.python.org/pep-0646/#summary-examples

      Тут более удачные примеры


      1. Megadeth77
        00.00.0000 00:00

        Спасибо


    1. iroln
      00.00.0000 00:00
      +3

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


      Статью писал безграмотный копирайтер, не особо внимательный и погружённый в тему.


      транспонирование кода из второго питона в третий

      Транспонирование кода — это что-то новенькое. :)


      1. andgein
        00.00.0000 00:00
        +2

        Ответственность за «транспонирование» беру полностью на себя! Это я в докладе сказал это слово :)) (пруф: https://youtu.be/osVUoYNAumA?t=468)

        Термина такого действительно нет, не знаю, почему именно оно пришло мне в голову. Ну приглючило, бывает :)

        Давайте заменим на «перевод».

        Картинку тоже попросил заменить на актуальную, спасибо большое за внимательность!