Python 3.12: новые возможности и синтаксис

16 октября 2026
Автор

Антон Ларичев

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

Python 3.12 вышел в октябре 2023 года и принёс ряд значительных улучшений: новый синтаксис для обобщённых типов, расширенные возможности f-строк, улучшенные сообщения об ошибках и новые инструменты для работы с типами. В этой статье разберём каждое нововведение с практическими примерами.

Курс по теме

Научиться программировать на Python

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Новый синтаксис обобщённых типов (PEP 695)

Одно из самых заметных изменений — новый способ объявления обобщённых (generic) функций, классов и псевдонимов типов. До Python 3.12 для этого требовалось импортировать TypeVar из модуля typing и явно передавать его в Generic.

Как было до Python 3.12

from typing import TypeVar, Generic

T = TypeVar("T")
K = TypeVar("K")
V = TypeVar("V")


class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()


def first(items: list[T]) -> T:
    return items[0]


def zip_dicts(keys: list[K], values: list[V]) -> dict[K, V]:
    return dict(zip(keys, values))

Новый синтаксис в Python 3.12

class Stack[T]:
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()


def first[T](items: list[T]) -> T:
    return items[0]


def zip_dicts[K, V](keys: list[K], values: list[V]) -> dict[K, V]:
    return dict(zip(keys, values))

Теперь переменные типа объявляются прямо в угловых скобках — импортировать TypeVar и Generic больше не нужно. Код становится компактнее и читается естественнее.

Оператор type для псевдонимов типов

PEP 695 также вводит новый оператор type для создания псевдонимов типов:

# До Python 3.12
from typing import TypeAlias

Vector: TypeAlias = list[float]
Matrix: TypeAlias = list[list[float]]

# Python 3.12
type Vector = list[float]
type Matrix = list[list[float]]

# Обобщённый псевдоним
type Pair[T] = tuple[T, T]
type Callback[T] = callable[[T], None]

Псевдонимы через type вычисляются лениво (lazy evaluation), что позволяет создавать рекурсивные определения:

type Tree[T] = T | list["Tree[T]"]

def flatten(tree: Tree[int]) -> list[int]:
    if isinstance(tree, list):
        result = []
        for item in tree:
            result.extend(flatten(item))
        return result
    return [tree]

print(flatten([1, [2, [3, 4]], 5]))  # [1, 2, 3, 4, 5]

Ограничения и протоколы для TypeVar

При новом синтаксисе можно задавать ограничения и bound прямо в объявлении:

from typing import Protocol


class Comparable(Protocol):
    def __lt__(self, other: "Comparable") -> bool: ...


# Ограничение: T должен реализовывать Comparable
def max_value[T: Comparable](items: list[T]) -> T:
    return max(items)


# Ограничение набором типов
def to_string[T: (int, float, str)](value: T) -> str:
    return str(value)

Улучшения f-строк (PEP 701)

F-строки существуют с Python 3.6, но в 3.12 их парсер был полностью переписан, что сняло ряд ограничений.

Вложенные кавычки

Раньше кавычки внутри f-строки не могли совпадать с внешними. Теперь это ограничение снято:

# До Python 3.12 — нужно было менять кавычки или использовать переменную
name = "Alice"
greeting = f"Hello, {name.upper()!r}"

# Теперь можно использовать те же кавычки внутри
users = ["Alice", "Bob", "Charlie"]
message = f"First user: {users[0]}"

# Кавычки в выражениях
report = f"Users: {', '.join([u.upper() for u in users])}"
print(report)  # Users: ALICE, BOB, CHARLIE

# Многострочные f-строки с произвольными кавычками
data = {"key": "value", "count": 42}
summary = f"""
Data summary:
  Key: {data["key"]}
  Count: {data["count"]}
"""

Вложенные f-строки

width = 10
numbers = [1, 2, 3, 4, 5]

# Теперь вложенные f-строки работают без ограничений
formatted = f"{f"{', '.join(str(n) for n in numbers)}".center(width * 3)}"
print(formatted)

# Форматирование с динамически вычисленными параметрами
precision = 4
value = 3.14159265
result = f"{value:{f'.{precision}f'}}"
print(result)  # 3.1416

Многострочные выражения и комментарии

products = [
    {"name": "Laptop", "price": 999.99},
    {"name": "Mouse", "price": 29.99},
]

# Многострочное выражение внутри f-строки
total = f"""Total: {
    sum(
        p["price"]  # цена каждого товара
        for p in products
    ):.2f
}"""
print(total)  # Total: 1029.98

Декоратор @override (PEP 698)

Python 3.12 добавляет декоратор @override в модуль typing. Он сигнализирует статическим анализаторам (mypy, pyright), что метод переопределяет метод из родительского класса.

from typing import override


class Animal:
    def speak(self) -> str:
        return "..."

    def move(self) -> str:
        return "moving"


class Dog(Animal):
    @override
    def speak(self) -> str:  # OK — метод существует в Animal
        return "Woof!"

    @override
    def eat(self) -> str:  # Ошибка! Метода eat нет в Animal
        return "nom nom"

Без @override переопределение метода с опечаткой в имени не даст ошибки, и новый метод просто добавится к классу, а не переопределит родительский:

class Cat(Animal):
    # Опечатка: speek вместо speak — без @override ошибка не будет обнаружена
    def speek(self) -> str:
        return "Meow!"


cat = Cat()
print(cat.speak())  # "..." — родительский метод, а не переопределённый!

С @override статический анализатор сразу укажет на проблему.

Улучшенные сообщения об ошибках

Python 3.12 продолжает линию на читаемые сообщения об ошибках, начатую в 3.10.

Ошибки в import

# Python 3.12 предлагает подсказки при ImportError
import colections  # Вы имели в виду: collections?

# ModuleNotFoundError: No module named 'colections'.
# Did you mean: 'collections'?

Более точные указатели в traceback

def get_user_city(data):
    return data["user"]["address"]["city"]

user_data = {"user": {"address": None}}
get_user_city(user_data)

# Python 3.12 traceback:
# TypeError: 'NoneType' object is not subscriptable
#     return data["user"]["address"]["city"]
#            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^

Стрелки теперь точно указывают на проблемное выражение внутри цепочки вызовов.

Ошибки при работе с атрибутами

class Config:
    debug = False
    database_url = "postgresql://localhost/mydb"
    max_connections = 10


config = Config()
print(config.databse_url)  # Опечатка

# AttributeError: 'Config' object has no attribute 'databse_url'.
# Did you mean: 'database_url'?

Новые методы pathlib.Path

Модуль pathlib получил два новых метода в Python 3.12.

Path.relativeto с walkup

from pathlib import Path

path = Path("/home/user/projects/app/src/main.py")
base = Path("/home/user/projects")

# До Python 3.12 — только для подпутей
relative = path.relative_to(base)
print(relative)  # app/src/main.py

# Python 3.12 — walk_up позволяет подниматься выше
base2 = Path("/home/user/projects/other")
relative2 = path.relative_to(base2, walk_up=True)
print(relative2)  # ../app/src/main.py

Path.isrelativeto

from pathlib import Path

path = Path("/home/user/documents/report.pdf")

print(path.is_relative_to("/home/user"))       # True
print(path.is_relative_to("/home/admin"))      # False
print(path.is_relative_to("/home/user/docs"))  # False

# Полезно для проверки, находится ли файл в разрешённой директории
def safe_read(file_path: Path, allowed_base: Path) -> str:
    if not file_path.is_relative_to(allowed_base):
        raise PermissionError(f"Access denied: {file_path}")
    return file_path.read_text()

Новый API мониторинга (PEP 669)

Python 3.12 вводит sys.monitoring — низкоуровневый API для отслеживания выполнения программы. Он заменяет устаревший sys.settrace и работает значительно быстрее.

import sys

# Регистрируем инструмент мониторинга
MY_TOOL_ID = sys.monitoring.DEBUGGER_ID
sys.monitoring.use_tool_id(MY_TOOL_ID, "my_profiler")

call_counts: dict[str, int] = {}


def handle_call(code, instruction_offset):
    name = code.co_qualname
    call_counts[name] = call_counts.get(name, 0) + 1


# Подписываемся на событие вызова функции
sys.monitoring.set_events(MY_TOOL_ID, sys.monitoring.events.CALL)
sys.monitoring.register_callback(
    MY_TOOL_ID,
    sys.monitoring.events.CALL,
    handle_call,
)


def fibonacci(n: int) -> int:
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


result = fibonacci(10)
sys.monitoring.set_events(MY_TOOL_ID, 0)  # Отключаем мониторинг

print(f"fibonacci(10) = {result}")
print(f"Вызовов fibonacci: {call_counts.get('fibonacci', 0)}")
# fibonacci(10) = 55
# Вызовов fibonacci: 177

Удаление distutils

В Python 3.12 окончательно удалён модуль distutils, объявленный устаревшим в 3.10. Если ваш код или зависимости используют distutils, их нужно обновить:

# Было (не работает в Python 3.12)
from distutils.version import LooseVersion
from distutils.util import strtobool

# Заменяем на
from packaging.version import Version  # pip install packaging

# strtobool заменяем собственной функцией или используем:
def strtobool(val: str) -> bool:
    val = val.lower()
    if val in ("y", "yes", "t", "true", "on", "1"):
        return True
    if val in ("n", "no", "f", "false", "off", "0"):
        return False
    raise ValueError(f"Invalid truth value: {val!r}")

Улучшения производительности

Python 3.12 продолжает работу проекта Faster CPython. Среди ключевых улучшений:

  • Ускорение вызовов функций за счёт оптимизации внутреннего фрейма
  • Снижение накладных расходов на работу с comprehensions (они больше не создают вложенный фрейм)
  • Оптимизация работы с dict и list
  • Улучшенный механизм специализации байткода (adaptive interpreter)
import timeit

# List comprehension в Python 3.12 быстрее,
# потому что не создаёт отдельный фрейм
time_comp = timeit.timeit(
    "[x * x for x in range(1000)]",
    number=10000,
)
print(f"List comprehension: {time_comp:.3f}s")

Краткий итог нововведений

Нововведение PEP Описание
Синтаксис обобщённых типов 695 class Box[T]:, def first[T](...), type Alias = ...
Улучшения f-строк 701 Вложенные кавычки, комментарии, многострочные выражения
Декоратор @override 698 Явная пометка переопределённых методов
sys.monitoring 669 Новый быстрый API для профилировщиков и отладчиков
Улучшенные ошибки — Подсказки при опечатках, точные указатели в traceback
Удаление distutils — Финальное удаление; используйте setuptools или packaging

Python 3.12 делает код с обобщёнными типами значительно чище, расширяет возможности f-строк и укрепляет инструментарий статической типизации. Если вы ещё не перешли на эту версию, самое время это сделать.

Чтобы освоить современный Python с нуля и научиться писать чистый, типизированный код, записывайтесь на курс — Python-разработчик на PurpleSchool.

Стрелочка влевоPython Enum — перечисления в Pythonpytest: фикстуры и параметризация тестовСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Как отправлять запросы с помощью requests в PythonКак работает команда print в PythonВозможности Python для автоматизации задачРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonРабота с данными через API и внешние сервисыОсновы Django с PythonСтруктура и оформление кода PythonПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonFastAPI Python — быстрый старт: создание REST API с нуляКак выполнять HTTPS-запросы в PythonКак работать с API в PythonПочему Python выводит значение без команды print
Ввод целого числа в PythonВедение логов в PythonУдаление данных в Python с помощью removeОбработка исключений с помощью try/except в PythonФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonКак скачать Python на компьютерПростая программа на Python для начинающихОсновы Python для тех, кто начинаетЧто нового в Python 3Поддерживается ли Python 2 и стоит ли его использоватьPython 1 — с чего начиналась история языкаКоманда python print - полное руководство по выводу данныхПравила именования переменных в PythonПользовательские исключения в PythonОсновы Python coreОписание объектов PythonНаследование классов в Python — основы и примерыМножественное наследование в Python — примеры и MROКомментарии в Python — однострочные, многострочные и docstringКакой Python выбрать для установкиКонтекстный менеджер with в Python — как работает и зачем нуженКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак получить последний элемент в PythonКак пользоваться консолью PythonКак найти значение в PythonКак настроить PythonКак использовать print для строк в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonЦелые числа в PythonАбстрактные классы в Python — ABC и abstractmethod
uv — современный менеджер пакетов PythonPython subprocess: запуск внешних команд и процессовSQLModel ORM для FastAPIВеб-скрапинг с Scrapy в PythonPython rich: красивый вывод в терминалеPydantic: валидация данных в PythonPoetry: управление зависимостями в PythonPython pathlib — современная работа с файловыми путямиPython click: создание CLI-приложенийPython itertools — работа с итераторамиПарсинг сайтов с BeautifulSoup в PythonPython argparse: разбор аргументов командной строкиAlembic для миграций базы данных с SQLAlchemy
Загрузка данных PythonУправление проектами на GitHub с PythonСоздание веб-приложений на Flask PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание игр с PygameСоздание GUI в PythonКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timeМатематические операции в Python с модулем mathPython listing — что это и как использоватьPython name — особенности переменнойPytest — тестирование на Python: полное руководствоОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахМашинное обучение с PythonИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОбработка чисел, введённых через input в PythonОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПродвинутые генераторы в Python — send, throw, close и корутиныОбъявление переменных и управление областью видимости в PythonПозиционные и именованные аргументы в PythonПередача аргументов по ссылке и по значению в PythonПередача аргументов через args и kwargs в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак работает функция any и all в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию filter в PythonКак использовать функцию eval безопасно в PythonКак использовать функцию isinstance в PythonКак использовать декораторы в PythonИзменяемые и неизменяемые типы данных в PythonКак использовать функцию filter в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто такое замыкания в PythonЧто делает функция reduce в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУстановка pip в PythonУправление библиотеками с помощью Python PackagingУдаление пробелов с помощью strip в PythonУправление зависимостями requirement в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРабота с Unicode кодировками в PythonРазбор Program Files в PythonРабота с системными функциями Python sysРабота с модулем logging в PythonРабота с каталогами в PythonРабота с папкой AppData в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеИмпорт модулей в Python и правила подключенияРабота с файлами в Python пошаговоЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeПеременные среды в PythonСборка проекта с помощью packaging в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonИмпорт имен в PythonСреда IDLE Python и базовые возможностиЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonТипы данных в Python — обзор и рекомендацииОсновные операции со строками в PythonМетоды str в Python и обработка текстаСписки в Python и их ключевые методыСоздание списков данных в PythonРабота со строками и символами в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с элементами данных PythonРабота с двоичными числами PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с большими числами в PythonРабота с битами в PythonРабота с байтами в PythonЧто такое значение в Python и как его определитьМножества в Python и операции с нимиИспользование range в Python для цикловПроверка на четность в PythonПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОпределение индекса элемента в PythonОкругление чисел в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКак вычислить сумму чисел в PythonКак получить остаток от деления в PythonКак найти следующее число в PythonИспользование Unicode в PythonТип int в Python и его особенностиИндекс списка в PythonКортежи данных в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonРабота с данными в Python на практикеКак работать с числами в Python
Телеграм-бот на Python: от команды до деплояRedis в Python с asyncioPython multiprocessing: параллельные процессыMotor: асинхронная работа с MongoDB в PythonPython httpx: современный HTTP-клиентFastAPI WebSockets в реальном времениFastAPI middleware и обработка запросовFastAPI с JWT аутентификациейconcurrent.futures: параллельные задачи в PythonФоновые задачи с Celery в PythonАсинхронная работа с PostgreSQL в Python через asyncpgPython asyncio.TaskGroupСинтаксис async/await в Python — как писать асинхронный кодБиблиотека aiohttp в Python — асинхронные HTTP-запросыМодуль asyncio в Python — основы асинхронного программирования
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий