Стала доступна для скачивания или установки из репозиториев новая версия Python 3.11.0.
Python 3.11 стал быстрее!
В данном релизе разработчики сконцентрировались на увеличении скорости работы интерпретатора CPython — основного средства запуска кода на языке программирования Python. В зависимости от тестов производительность версии 3.11 выше предыдущей 3.10 на 10-60 процентов. Условно можно сказать, что в среднем новая версия будет в 1.25 раз быстрее.
Ранее уже сообщалось, что бета-версия 3.11 обыгрывает 3.10.4 (актуальную стабильную на тот момент версию) в среднем на 25%. Также указывалось, что есть планы продолжать "ускорять" Python.
Новые возможности: группы исключений (ExceptionGroups)
В Python 3.11 добавляется новый стандартный тип исключений — ExceptionGroups
,
а также синтаксическая конструкция except*
— для обработки этих самых
групп исключений.
Общая идея в следующем: сейчас в интерпретаторе можно пробросить только одно
исключение за раз. Есть вариант привязывать одно исключение к другому через raise ... from ...
,
но тогда они скорее "причина" основного исключения.
Есть ситуации, где нужно пробросить множество несвязанных исключений. К примеру, при конкурентной работе происходят ошибки в разных корутинах — какую из них возвращать? Они не знают, что они как-то связаны, но, скорее всего, именно во взаимодействии и появилась "исключительная ситуация".
Так у нас появляется потребность в групповых исключениях: BaseExceptionGroup(BaseException)
и ExceptionGroup(BaseExceptionGroup, Exception)
— по аналогии с иерархией обычных исключений.
Оба они имеют два позиционных параметра: сообщение, список исключений.
>>> eg = ExceptionGroup(
... "one",
... [
... TypeError(1),
... ExceptionGroup(
... "two",
... [TypeError(2), ValueError(3)]
... ),
... ExceptionGroup(
... "three",
... [OSError(4)]
... )
... ]
... )
>>> import traceback
>>> traceback.print_exception(eg)
| ExceptionGroup: one (3 sub-exceptions)
+-+---------------- 1 ----------------
| TypeError: 1
+---------------- 2 ----------------
| ExceptionGroup: two (2 sub-exceptions)
+-+---------------- 1 ----------------
| TypeError: 2
+---------------- 2 ----------------
| ValueError: 3
+------------------------------------
+---------------- 3 ----------------
| ExceptionGroup: three (1 sub-exception)
+-+---------------- 1 ----------------
| OSError: 4
+------------------------------------
Из них можно получать подгруппы, разделять по условию и т.д. Подробнее в PEP-0654.
except*
работает как "распаковка" группы исключений для сопоставления: какой
обработчик применить. Например:
>>> try:
... raise ExceptionGroup("problem", [BlockingIOError()])
... except* OSError as e: # Как и с except - родительское исключени также матчится
... print(repr(e))
... except* BlockingIOError: # Если бы не except выше, вызвался бы этот обработчик
... print('never')
...
ExceptionGroup('problem', [BlockingIOError()])
Улучшенное отображение ошибок
В Python 3.11 постарались улучшить систему подсказок о том, в каком месте произошла ошибка. Думаю, все программисты на Python это скоро заметят и порадуются. Самим же "пользователям" CPython ничего для этого делать особо не надо, поэтому ограничимся примером. Есть у нас код:
x['a']['b']['c']['d'] = 1
При запуске раньше мы могли получить ошибку:
Traceback (most recent call last):
File "test.py", line 2, in <module>
x['a']['b']['c']['d'] = 1
TypeError: 'NoneType' object is not subscriptable
После чего ругались: какой же элемент из этой цепочки был None? С Python 3.11 вывод будет следующим:
Traceback (most recent call last):
File "test.py", line 2, in <module>
x['a']['b']['c']['d'] = 1
~~~~~~~~~~~^^^^^
TypeError: 'NoneType' object is not subscriptable
— по стрелочкам теперь ясно, что None лежал в x['a']['b']['c']
.
Новый тип данных — Self
Думаю, всем, кто пользуется type-hinting-ом, уже давно надоело для порождающих и прочих методов, что используют объекты этого же класса в аргументах, указывать тип значения в кавычках. Поясню на примере:
class Shape:
def set_scale(self, scale: float) -> 'Shape':
self.scale = scale
return self
— так IDE понимает, что возвращается тип Shape
, но написать без кавычек
мы не можем, ведь на этот момент класс Shape
ещё не определён... Вот и появился
"костыль" с кавычками.
Теперь можно указать тип возвращаемого значения без кавычек:
from typing import Self
class Shape:
def set_scale(self, scale: float) -> Self:
self.scale = scale
return self
И ещё несколько новшеств
Выше я описал те нововведения, которое порадовали меня больше остальных. Но есть и другие, о которых также стоит упомянуть.
В частности, модуль typing
:
TypeVarTuple
позволяет описывать "вариативные дженерики", с ними можно описывать сразу несколько типов.- Появился тип
LiteralString
, который соответствует строковой константе. Required
/NotRequired
для обозначения обязательных / не обязательных полейTypedDict
.
Добавлен класс asyncio.TaskGroup
, позволяющий, используя синтаксис асинхронного контекстного менеджера,
дождаться исполнения запущенных в нём корутин:
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(some_coro(...))
task2 = tg.create_task(another_coro(...))
print("На этот момент оба таска завершены.")
Для регулярок подвезли ревнивую (сверхжадную) квантификацию и атомарную группировку.
Для любителей dataclass-ов (а всё, кажется, к ним и идёт, если нужно описывать модели)
добавили dataclass_transform
, позволяющий лучше настроить поведение dataclass-ов.
Авторы PEP-0681 явно вдохновлялись проектами attrs, pydantic,
да и в целом ORM-ками.
Добавлен новый модуль tomllib
для работы с форматом TOML подобно тому, как сделали с json, yaml.
Думаю, это основные изменения, которые пришли к нам с выходом Python 3.11. За более подробным списком можно сходить на официальную страницу со списком изменений.