Cat

Что нового в Python 3.12

Перевод ключевых изменений и нововведений в Python 3.12.

Реклама

Icon Link
Python proDream 03 Октябрь 2023 Просмотров: 873

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&nbsp;<module>&nbsp;import&nbsp;<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.)

Автор

    Нет комментариев

    Реклама