Вышел Python 3.11

Новый релиз включает скорость, новый вывод трейсбеков, группы исключений, tomllib
, группы задач asyncio
, плюс расширения в регулярные выражения и тайп хинты.
Сообщение о новом релизе опубликовали на Python.org.
Ниже я опишу нововведения подробнее.
Улучшение производительности
Скорость работы 3.11 увеличилась в среднем на 25%, а время старта уменьшилось на 10-60%.
Описание оптимизаций CPython доступно в документации.
Указка на ошибку в трейсбеке
Компилятор CPython поумнел и теперь умеет преобразовывать указатель на ошибку в байт-коде в ссылку на ошибку в исходниках. Трейсбеки читаются лучше.
Исключение в следующем коде:
def foo(x):
1 + 1/0 + 2
def bar(x):
1 + foo(x) + foo(x)
bar(bar(bar(2)))
Выводится так:
Traceback (most recent call last):
File "test.py", line 7, in <module>
bar(bar(bar(2)))
^^^^^^
File "test.py", line 5, in bar
1 + foo(x) + foo(x)
^^^^^^
File "test.py", line 2, in foo
1 + 1/0 + 2
~^~
ZeroDivisionError: division by zero
Группы исключений и обработка нескольких исключений одновременно
Новые классы ExceptionGroup
и BaseExceptionGroup
объединяют несколько исключений в одну древовидную структуру:
>>> 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
+------------------------------------
Такую группу исключений можно выбросить вверх по стеку и поймать как стандартное исключение. По классу какого-либо исключения в дереве группы ловить группу нельзя:
try:
raise ExceptionGroup("test group", [ValueError("value error"), OSError("os error")])
except ValueError as e:
# Не вызовется
print(f"ValueError handled: {e}")
except OSError as e:
# Не вызовется
print(f"OSError handled: {e}")
+ Exception Group Traceback (most recent call last):
| File "test.py", line 2, in <module>
| raise ExceptionGroup("test group", [ValueError("value error"), OSError("os error")])
| ExceptionGroup: test group (2 sub-exceptions)
+-+---------------- 1 ----------------
| ValueError: value error
+---------------- 2 ----------------
| OSError: os error
+------------------------------------
Но с новым синтаксисом except*
можно ловить группы по содержимому их дерева.
try:
raise ExceptionGroup("test group", [ValueError("value error"), OSError("os error")])
except* ValueError as e:
# Не вызовется
print(f"ValueError handled: {e}")
except* OSError as e:
# Не вызовется
print(f"OSError handled: {e}")
ValueError handled: test group (1 sub-exception)
OSError handled: test group (1 sub-exception)
Что интересно, блоков except*
может выполниться несколько, если они все подходят.
Парсер формата TOML включен в стандартную библиотеку
Модуль предоставляет публичные функции load
и loads
наподобие модуля json
.
Пример файла TOML:
[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }
Пример парсинга:
import tomllib
with open("t.toml", "rb") as f:
print(tomllib.load(f))
{'database': {'enabled': True, 'ports': [8000, 8001, 8002], 'data': [['delta', 'phi'], [3.14]], 'temp_targets': {'cpu': 79.5, 'case': 72.0}}}
Группы асинхронных задач
asyncio.TaskGroup
- замена asyncio.gather
, которая позволяет планировать задачи, выполнять произвольный код между планированием, отменять задачи, если любая из них падает с ошибкой, и собирать ошибки из задач в одну группу исключений.
Новый API хорошо сочетается с ExceptionGroup
:
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(task1())
await do_something_else()
tg.create_task(task2())
except* ValueError as e:
logger.error("ValueError has occured")
Атомарные группы в регулярных выражениях
Новый релиз добавляет в re
поддержку синтаксиса атомарных групп ((?>...)
) и "possesive quantifiers" (подвариант атомарных групп).
Логика работы атомарных групп слишком сложна для этого поста. В официальном issue атомарных групп ссылаются на описание здесь.
Расширения в тайп хинты
- Новый тип
Self
позволяет унаследованным методам ссылаться на унаследовавшие их подклассы. TypeVarTuple
позволяет писать генерики с переменных числом параметров.LiteralString
- новый тип для строки-литерала.- Возможность указывать в
TypedDict
, какие ключи обязательны, а какие могут отсутствовать с помощьюRequired
иNotRequired
. - "Data Class Transforms" - новый API, обобщающий типизацию dataclass'ов, чтобы внешние фреймворки вроде Pydantic, SQLAlchemy, Django и др. могли реализовать поддержку тайп-хинтов в своих динамически-генерируемых типах.
Материал подготовлен с ❤️ редакцией Кухни IT.