Что нового в Python 3.12
Перевод ключевых изменений и нововведений в Python 3.12.
Реклама
Python 3.12 - это последний стабильный релиз языка программирования Python, который содержит изменения как в самом языке, так и в стандартной библиотеке. Изменения в библиотеке сосредоточены на удалении устаревших API, удобстве использования и корректности. Стоит отметить, что пакет distutils
был удален из стандартной библиотеки. Поддержка файловой системы в модулях os
и pathlib
получила ряд улучшений, а некоторые модули стали работать быстрее.
Изменения в языке являются сосредоточены на удобстве использования, так f-строки
лишились многих ограничений, а предложения "Did you mean ... "
продолжают улучшаться. Новый синтаксис параметров типов и оператор type
улучшают эргономику использования обобщенных типов и псевдонимов типов с проверками типов на этапе компиляции.
Эта статья не претендует на полную спецификацию всех новых функций, но предоставляет удобный обзор. Полная информация содержится в документации, такой как Library Reference и Language Reference. Если вы хотите понять полную реализацию и обоснование дизайна изменения, обратитесь к PEP для конкретной новой функции. Однако, обратите внимание, что PEP обычно не обновляется после полной реализации функции.
Полная версия изменений доступна на сайте: https://docs.python.org/3.13/whatsnew/3.12.html#whatsnew312-pep695
Новые функции синтаксиса:
- PEP 695 - синтаксис типовых параметров и оператор
type
Новые функции грамматики:
- PEP 701 -
f-строки
в грамматике
Улучшения интерпретатора:
- PEP 684 - уникальный
GIL
для каждого субинтерпретатора - PEP 669 - мониторинг с низким воздействием
- Улучшены предложения
"Did you mean..."
для исключенийNameError
,ImportError
иSyntaxError
Улучшения модели данных Python:
- PEP 688 - использование протокола буфера из Python
Значительные улучшения в стандартной библиотеке:
- Класс
pathlib.Path
теперь поддерживает наследование - Модуль
os
получил несколько улучшений для поддержки Windows - В модуль
sqlite3
добавлен интерфейс командной строки (CLI) - Проверка
isinstance()
по протоколам, проверяемым во время выполнения, ускорена от двух до 20 раз. - Пакет
asyncio
был улучшен в плане производительности, некоторые тесты показали ускорение на 75%. - В модуль
uuid
добавлен интерфейс командной строки (CLI) - Из-за изменений в PEP 701, генерация токенов через модуль
tokenize
стала быстрее на 64%.
Улучшения безопасности:
- Заменены реализации хеш-функций SHA1, SHA3, SHA2-384, SHA2-512 и MD5 встроенных в модуль
hashlib
на формально проверенный код из проектаHACL*
. Эти реализации остаются как запасные варианты, используемые только в том случае, если OpenSSL не предоставляет соответствующую функцию.
Улучшения C API:
- PEP 697, нестабильный уровень C API
- PEP 683, бессмертные объекты.
Улучшения реализации CPython:
- PEP 709, встраивание выражений
- Поддержка
CPython
для профилировщикаLinux perf
- Реализация защиты от переполнения стека на поддерживаемых платформах
Новые функции типизации:
- PEP 692, использование
TypedDict
для аннотирования**kwargs
- PEP 698, декоратор
typing.override()
Важные устаревания, удаления или ограничения:
- PEP 623: Удаление
wstr
из объектов Unicode в C API Python, сокращение размера каждого объекта str как минимум на 8 байт. - PEP 632: Удаление пакета
distutils
. Для замены предоставленных им API смотрите руководство по миграции. Сторонний пакетSetuptools
продолжает предоставлятьdistutils
, если он всё еще нужен в Python 3.12 и выше. - gh-95299: Не предустанавливается
setuptools
в виртуальных средах, созданных с помощьюvenv
. Это значит, чтоdistutils
,setuptools
,pkg_resources
иeasy_install
больше не будут доступны по умолчанию; чтобы получить к ним доступ, запуститеpip install setuptools
в активированной виртуальной среде. - Модули
asynchat
,asyncore
иimp
были удалены, а также несколько псевдонимов методовunittest.TestCase
.
Новые возможности
PEP 695: Синтаксис параметра типа.
Обобщенные классы и функции, определенные согласно PEP 484, использовали громоздкий синтаксис, который неясно описывал область действия параметров типа и требовал явного объявления различий.
PEP 695 вводит новый, более компактный и явный способ создания обобщенных классов и функций:
def max[T](args: Iterable[T]) -> T:
...
class list[T]:
def __getitem__(self, index: int, /) -> T:
...
def append(self, element: T) -> None:
...
Кроме того, PEP вводит новый способ объявления псевдонимов типов с использованием оператора type
, который создает экземпляр TypeAliasType
:
type Point = tuple[float, float]
Псевдонимы типов также могут быть обобщенными:
type Point[T] = tuple[T, T]
Новый синтаксис позволяет объявлять параметры TypeVarTuple
и ParamSpec
, а также параметры TypeVar
с границами или ограничениями:
type IntFunc[**P] = Callable[P, int] # ParamSpec
type LabeledTuple[*Ts] = tuple[str, *Ts] # TypeVarTuple
type HashableSequence[T: Hashable] = Sequence[T] # TypeVar с привязкой
type IntOrStrSequence[T: (int, str)] = Sequence[T] # TypeVar с ограничениями.
Значения псевдонимов типов, а так же привязки и ограничения переменных типа, созданные с помощью этого синтаксиса, вычисляются только по запросу (см. ленивое вычисление). Это означает, что псевдонимы типов могут ссылаться на другие типы, определенные позже в файле.
Параметры типа, объявленные через список параметров типа, видны в рамках объявления и любых вложенных областей, но не видны во внешней области. Например, они могут использоваться в аннотациях типа для методов обобщенного класса или в теле класса. Однако после определения класса их нельзя использовать в области модуля. См. Списки параметров типа для подробного описания времени выполнения семантики параметров типа.
Чтобы поддержать эти семантики области видимости, вводится новый тип области действия - область аннотаций. Области аннотаций ведут себя в большинстве случаев как области действия функций, но взаимодействуют иначе с охватывающими областями классов. В Python 3.13 аннотации также будут вычисляться в областях аннотаций.
См. PEP 695 для получения более подробной информации.
(PEP написан Эриком Траутом. Реализация - Джелле Зейлстра, Эрик Траут и другими в gh-103764.)
PEP 701: Синтаксическая формализация f-строк.
PEP 701 снимает некоторые ограничения на использование f-строк. Теперь выражения внутри f-строк могут быть любыми допустимыми выражениями Python, включая строки, повторно использующие тот же тип кавычек, что и содержащая f-строка, многострочные выражения, комментарии, обратные слеши и символы юникода. Давайте рассмотрим это подробнее:
Повторное использование кавычек:
В Python 3.11 повторное использование тех же кавычек, что и окружающие f-строку, вызывает ошибку синтаксиса, заставляя пользователя использовать другие доступные кавычки (например, двойные кавычки или тройные кавычки, если f-строка использует одинарные кавычки). В Python 3.12 теперь можно делать так:
>>> songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
>>> f"This is the playlist: {", ".join(songs)}"
'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
Обратите внимание, что до этого изменения не было явного ограничения на вложенность f-строк, но невозможность повторного использования кавычек строк внутри компонента выражения f-строк делала невозможным произвольное вложение f-строк. Фактически, это самая вложенная f-строка, которую можно было написать:
>>> f"""{f'''{f'{f"{1+1}"}'}'''}"""
'2'
Так как теперь f-строки могут содержать любые допустимые выражения Python внутри компонентов выражения, теперь возможно произвольное вложение f-строк:
>>> f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"
'2'
Многострочные выражения и комментарии:
В Python 3.11 выражения f-строк должны быть определены в одной строке, даже если выражение внутри f-строк обычно может занимать несколько строк (например, при определении литеральных списков на нескольких строках), что делает их сложнее для чтения. В Python 3.12 теперь можно определять многострочные f-строки и добавлять встроенные комментарии:
>>> f"This is the playlist: {", ".join([
... 'Take me back to Eden', # My, my, those eyes like fire
... 'Alkaline', # Not acid nor alkaline
... 'Ascensionism' # Take to the broken skies at last
... ])}"
'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
Обратные слэши и символы Unicode:
До версии Python 3.12 выражения f-строк не могли содержать символы экранирования \
. Это также затрагивало последовательности с символами Unicode (например, \N{snowman}
), так как они содержат часть \N
, которая ранее не могла быть частью компонентов выражений f-строк. Теперь вы можете определять выражения следующим образом:
>>> print(f"This is the playlist: {"\n".join(songs)}")
This is the playlist: Take me back to Eden
Alkaline
Ascensionism
>>> print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")
This is the playlist: Take me back to Eden♥Alkaline♥Ascensionism
См. PEP 701 для получения более подробной информации.
Положительным побочным эффектом реализации этой функции (путем анализа f-строк с использованием парсера PEG) является более точные сообщения об ошибках для f-строк, которые включают точное местоположение ошибки. Например, в Python 3.11 следующая f-строка вызывает SyntaxError
:
>>> my_string = f"{x z y}" + f"{1 + 1}"
File "<stdin>", line 1
(x z y)
^^^
SyntaxError: f-string: invalid syntax. Perhaps you forgot a comma?
Но сообщение об ошибке не содержит точного местоположения ошибки внутри строки и также имеет искусственно окруженное скобками выражение. В Python 3.12, так как f-строки разбираются с помощью парсера PEG, сообщения об ошибках могут быть более точными и показывать всю строку целиком:
>>> my_string = f"{x z y}" + f"{1 + 1}"
File "<stdin>", line 1
my_string = f"{x z y}" + f"{1 + 1}"
^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
(Содействие внесли Пабло Галиндо, Батухан Таскайя, Лисандрос Николау, Кристиан Маурейра-Фредес и Марта Гомес в gh-102856. PEP написан Пабло Галиндо, Батуханом Таскайей, Лисандросом Николау и Мартой Гомес).
PEP 684: GIL для каждого субинтерпретатора.
PEP 684 вводит GIL для каждого субинтерпретатора, так что теперь под-интерпретаторы могут создаваться с уникальным GIL для каждого интерпретатора. Это позволяет программам на Python полностью использовать множество ядер процессора. В настоящее время это доступно только через C-API, хотя ожидается, что в версии 3.13 появится Python API.
Для создания интерпретатора с собственным GIL используйте новую функцию Py_NewInterpreterFromConfig()
:
PyInterpreterConfig config = {
.check_multi_interp_extensions = 1,
.gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = NULL;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
if (PyStatus_Exception(status)) {
return -1;
}
/* The new interpreter is now active in the current thread. */
Для дополнительных примеров использования C-API для субинтерпретатора с индивидуальными GIL смотрите файл Modules/_xxsubinterpretersmodule.c
.
(Предоставлено Эриком Сноу в gh-104210 и др.)
PEP 669: Минимальное воздействие при мониторинге для CPython.
PEP 669 определяет новый API для профилировщиков, отладчиков и других инструментов для мониторинга событий в CPython. Он охватывает широкий спектр событий, включая вызовы, возвраты, строки, исключения, переходы и многое другое. Это означает, что вы платите только за то, что используете, обеспечивая поддержку отладчиков и инструментов для оценки покрытия с минимальной нагрузкой. Подробности смотрите в sys.monitoring
.
(Предоставлено Марком Шенноном в gh-103082.)
PEP 688: Доступ к протоколу буфера в Python.
PEP 688 представляет способ использования протокола буфера из кода Python. Теперь классы, которые реализуют метод __buffer__()
, могут использоваться как типы буфера.
Новый абстрактный базовый класс (ABC) collections.abc.Buffer
предоставляет стандартный способ представления объектов буфера, например, в аннотациях типов. Новое перечисление inspect.BufferFlags
представляет флаги, которые могут использоваться для настройки создания буфера.
(Предоставлено Jelle Zijlstra в gh-102500.)
PEP 709: Встраивание компрехеншенов.
Словари, списки и множества, созданные с использованием компрехеншенов, теперь встраиваются, а не создают новый объект функции, используемый только один раз для каждого выполнения компрехеншена. Это ускоряет выполнение компрехеншена до двух раз. Дополнительные детали смотрите в PEP 709.
Переменные итерации в компрехеншене остаются изолированными и не перезаписывают переменные с тем же именем во внешней области видимости, и они не видны после завершения компрехеншена. Встраивание приводит к нескольким видимым изменениям в поведении:
- Больше нет отдельного кадра для компрехеншена в трассировочных сообщениях, и трассировка/профилирование больше не показывает компрехеншен как вызов функции.
- Модуль
symtable
больше не будет создавать дочерние таблицы символов для каждого компрехеншена; вместо этого локальные переменные компрехеншена будут включены в таблицу символов родительской функции. - Вызов
locals()
внутри компрехеншена теперь включает переменные из внешней области видимости и больше не включает синтетическую переменную.0
для "аргумента" компрехеншена. - Компрехеншен, выполняющий итерацию напрямую по
locals()
(например,[k for k in locals()]
), может выдавать "RuntimeError: dictionary changed size during iteration" при выполнении под трассировкой (например, измерение покрытия кода). Это то же самое поведение, которое уже видно, например, в циклеfor k in locals():
. Чтобы избежать ошибки, сначала создайте список ключей для итерации:keys = list(locals()); [k for k in keys]
.
(Внесено вкладом Карлом Мейером и Владимиром Матвеевым в PEP 709.)
Улучшенные сообщения об ошибках.
- Теперь модули из стандартной библиотеки могут быть предложены как часть сообщений об ошибках, отображаемых интерпретатором, когда возникает исключение
NameError
на верхнем уровне.
(Предоставлено Пабло Галиндо в gh-98254.)
>>> sys.version_info
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined. Did you forget to import 'sys'?
- Улучшена рекомендация ошибки для исключений
NameError
для экземпляров. Теперь, еслиNameError
возникает в методе, и у экземпляра есть атрибут, который точно совпадает с именем в исключении, рекомендация будет включатьself.<NAME>
вместо ближайшего совпадения в области метода.
(Предоставлено Пабло Галиндо в gh-99139.)
>>> class A:
... def __init__(self):
... self.blech = 1
... def foo(self):
... somethin = blech
>>> A().foo()
Traceback (most recent call last):
File "<stdin>", line 1
somethin = blech
^^^^^
NameError: name 'blech' is not defined. Did you mean: 'self.blech'?
- Улучшено сообщение об ошибке
SyntaxError
, когда пользователь вводитimport x from y
вместоfrom y import x
.
(Предоставлено Пабло Галиндо в gh-98931.)
>>> import a.y.z from b.y.z
Traceback (most recent call last):
File "<stdin>", line 1
import a.y.z from b.y.z
^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: Did you mean to use 'from ... import ...' instead?
- Исключения
ImportError
, возникающие из-за неудачных инструкцийfrom <module> import <name>
, теперь включают рекомендации для значения<name>
на основе доступных имен в<module>
.
(Предоставлено Пабло Галиндо в gh-91058.)
>>> from collections import chainmap
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'chainmap' from 'collections'. Did you mean: 'ChainMap'?
PEP 692: Использование TypedDict для более точной типизации **kwargs
.
Типизация **kwargs
в сигнатуре функции, представленная в PEP 484, позволяла задавать допустимые аннотации только в тех случаях, где все **kwargs
были одного и того же типа.
PEP 692 устанавливает более точный способ типизации **kwargs
, используя типизированные словари:
from typing import TypedDict, Unpack
class Movie(TypedDict):
name: str
year: int
def foo(**kwargs: Unpack[Movie]): ...
Дополнительные подробности можно найти в PEP 692.
(Предоставлено Франеком Мажерой в gh-103629.)
PEP 698: Переопределение декоратора для статической типизации
В модуль typing
был добавлен новый декоратор typing.override()
. Он указывает статическим типизаторам, что метод предназначен для переопределения метода в суперклассе. Это позволяет статическим типизаторам обнаруживать ошибки, когда метод, предназначенный для переопределения в базовом классе, фактически этого не делает.
Пример:
from typing import override
class Base:
def get_color(self) -> str:
return "blue"
class GoodChild(Base):
@override # ok: overrides Base.get_color
def get_color(self) -> str:
return "yellow"
class BadChild(Base):
@override # type checker error: does not override Base.get_color
def get_colour(self) -> str:
return "red"
Дополнительные сведения см. в PEP 698.
(Предоставлено Стивеном Трокслером в gh-101561.)
Другие изменения в языке
- Теперь парсер генерирует
SyntaxError
при разборе исходного кода, содержащего нулевые байты.
(Предложено Пабло Галиндо в gh-96670.) - Пара символов с обратным слешем, не являющаяся допустимой последовательностью экранирования, теперь вызывает
SyntaxWarning
, а неDeprecationWarning
. Например,re.compile("\d+.\d+")
теперь генерируетSyntaxWarning
("\d"
- недопустимая последовательность экранирования, используйте сырые строки для регулярных выражений:re.compile(r"\d+.\d+")
). В будущих версиях Python, возможно, будет вызвано исключениеSyntaxError
, а неSyntaxWarning
.
(Предложено Виктором Штиннером в gh-98401.) - Восьмеричные экранированные последовательности со значением больше
0o377
(например,"\477"
), устаревшие в Python 3.11, теперь вызываютSyntaxWarning
, а неDeprecationWarning
. В будущих версиях Python они, возможно, вызовутSyntaxError
.
(Предложено Виктором Штиннером в gh-98401.) - Переменные, используемые в целевой части генераторных выражений и не сохраняемые, теперь могут быть использованы в выражениях присваивания (
:=
). Например, в[(b := 1) for a, b.prop in some_iter]
присваиваниеb
теперь разрешено. Обратите внимание, что присвоение переменным, сохраняемым в целевой части генераторных выражений (какa
), по-прежнему запрещено в соответствии с PEP 572. (Предложено Никитой Соболевым в gh-100581.) - Исключения, вызванные в методе
__set_name__
класса или типа, теперь не оборачиваются вRuntimeError
. Информация о контексте добавляется к исключению как замечание PEP 678.
(Предложено Ирит Катриэль в gh-77757.) - Когда конструкция
try-except*
обрабатывает всю группу исключенийExceptionGroup
и вызывает одно другое исключение, это исключение больше не оборачивается вExceptionGroup
. Также изменено в версии 3.11.4.
(Предоставлено Irit Katriel в gh-103590.) - Сборщик мусора теперь запускается только на механизме eval breaker петли оценки байт-кода Python, а не при выделении объектов. Сборка мусора также может выполняться при вызове
PyErr_CheckSignals()
, так что C-расширения, которым необходимо выполняться длительное время без выполнения какого-либо кода Python, также имеют шанс периодически выполнять сборку мусора.
(Предоставлено Pablo Galindo в gh-97922.) - Все встроенные и расширенные вызываемые объекты, ожидающие булевские параметры, теперь принимают аргументы любого типа, а не только
bool
иint
.
(Предоставлено Serhiy Storchaka в gh-60203.) - Теперь
memoryview
поддерживает полу-плавающий тип (код формата"e"
).
(Предоставлено Donghee Na и Antoine Pitrou в gh-90751.) - Объекты
slice
теперь хешируемы, что позволяет использовать их в качестве ключей словаря и элементов множества.
(Предоставлено Will Bradshaw, Furkan Onder и Raymond Hettinger в gh-101264.) - Функция
sum()
теперь использует суммирование Ноймайера для улучшения точности и коммутативности при суммировании чисел с плавающей запятой или смешанных целых и чисел с плавающей запятой.
(Предоставлено Raymond Hettinger в gh-100425.) ast.parse()
теперь вызываетSyntaxError
, а неValueError
при разборе исходного кода, содержащего нулевые байты.
(Предоставлено Pablo Galindo в gh-96670.)- У методов извлечения в модуле
tarfile
и функцииshutil.unpack_archive()
теперь есть новый аргументfilter
, который позволяет ограничивать функцииtar
, которые могут быть неожиданными или опасными, такими как создание файлов за пределами каталога назначения. См.tarfile extraction filters
для получения подробной информации. В Python 3.14 значение по умолчанию будет изменено на'data'
.
(Предоставлено Petr Viktorin в PEP 706.) - Экземпляры
types.MappingProxyType
теперь хешируемы, если базовое отображение хешируемо.
(Предоставлено Serhiy Storchaka в gh-87995.) - Добавлена поддержка профилировщика
perf
через новую переменную окруженияPYTHONPERFSUPPORT
и опцию командной строки-X perf
, а также новые функцииsys.activate_stack_trampoline()
,sys.deactivate_stack_trampoline()
иsys.is_stack_trampoline_active()
.
(Дизайн от Pablo Galindo. Содействие от Pablo Galindo и Christian Heimes с вкладами от Gregory P. Smith [Google] и Mark Shannon в gh-96123.)
Все статьи