Февральскую конференцию 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:
PEP 563 про отложенное исполнение аннотации позволил бы использовать в аннотации типов классы, не импортируя их. К сожалению, этот PEP отложили «навечно». Это не значит, что его не включат никогда , но это значит, что сейчас команда питона не готова назвать релиз, в котором они будут это делать.
И отклонили полностью PEP 677 про новый синтаксис написания типов для Callable в стиле функциональных языков:
(int, str) -> bool
.
Что известно про 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 году и давно не поддерживаются, надо чистить.
Еще немного новостей
Интересное исследование нашло несколько зловредных пакетов в PyPI, ворующих ваши креденшелы, разные важные файлы и настройки. Будьте аккуратны, не ставьте абы какие пакеты
Интересный проект, развития которого я очень жду, — это nogil. Ребята пытаются избавиться от того самого GIL, за который все хейтят питон после того, как перестают хейтить его за тормознутость. По сути,
nogil
— это форк CPython с попыткой отвязаться от GIL-а. На синтетических тестах многопоточные программы получают ускорение на 5-10%.Минутка безопасности. Ребята научились запускать
os.system()
благодаря проезду по памяти в CPython, не вызывая при этом, собственно, не вызываяos.system()
.
Ruff
Ещё я в каждом выпуске новостей рассказываю о сторонних проектах, которые меня заинтересовали. Обо всех проектах можно послушать в полной записи доклада, здесь же напишем о Ruff. Это линтер для питона, написанный на Rust, который рвёт по скорости все остальные линтеры:
Меня нельзя назвать большим любителем Rust, но я уверен, что именно так и надо использовать сильные стороны разных языков программирования. Если у вас есть быстрый Rust, то давайте писать на нем те части кода, в которых критично время. Давайте не хейтить языки друг друга, а искать точки соприкосновения и жить дружно ????
Это лишь часть выступления Андрея на конференции EkbPy, которая прошла в феврале в Екатеринбурге. Целиком его можно посмотреть на нашем YouTube-канале, как и десятки других крутых выступлений с наших конференций.
Присоединяйтесь к нашему сообществу! Ближайшая встреча питонистов пройдет 28–29 июля, на ежегодной конференции PyCon Russia 2023. Мы уже работаем над программой, подавайте заявки на доклады, бронируйте билеты и планируйте встречу.
Megadeth77
Спасибо, познавательно.
https://habrastorage.org/r/w1560/getpro/habr/upload_files/01a/21b/f36/01a21bf36206b6a31358b3980d311cfc.png
А в двух словах чем это отличается от обычных дженериков в 3.10?
Helltraitor
https://peps.python.org/pep-0646/#summary-examples
Тут более удачные примеры
Megadeth77
Спасибо
iroln
В статью запихнули неправильный пример. Этот пример в докладе приводится как то, что уже было раньше, а не как теперь можно.
Статью писал безграмотный копирайтер, не особо внимательный и погружённый в тему.
Транспонирование кода — это что-то новенькое. :)
andgein
Ответственность за «транспонирование» беру полностью на себя! Это я в докладе сказал это слово :)) (пруф: https://youtu.be/osVUoYNAumA?t=468)
Термина такого действительно нет, не знаю, почему именно оно пришло мне в голову. Ну приглючило, бывает :)
Давайте заменим на «перевод».
Картинку тоже попросил заменить на актуальную, спасибо большое за внимательность!