Сообщить об ошибке.

Класс ParamSpec модуля typing в Python

Переменные спецификации аргументов

Синтаксис:

import typing

# Новое в Python 3.10.
typing.ParamSpec(name, *, bound=None, covariant=False, contravariant=False)

# Использование
P = typing.ParamSpec('P')

Параметры:

  • name - имя переменной,
  • bound=None - связанный аргумент, аналогичный TypeVar,
  • covariant=False - ковариантный универсальный тип,
  • contravariant=False - контравариантный универсальный тип.

Описание typing.ParamSpec:

Класс ParamSpec() модуля typing представляет собой переменные спецификации аргументов. Другими словами это специализированная версия переменной типа typing.TypeVar.

С версии Python 3.12 спецификации параметров теперь можно объявлять с использованием синтаксиса параметра типа, представленного в PEP 695.

В списках параметров типа, спецификации параметров могут быть объявлены с помощью двух звездочек (**):

type IntFunc[**P] = Callable[P, int]

Для совместимости с Python 3.11 и более ранними версиями объекты ParamSpec также можно создавать следующим образом:

P = ParamSpec('P')

Переменные спецификации аргументов существуют в первую очередь для целей проверки статических типов. Они используются для пересылки типов параметров одного вызываемого объекта другому вызываемому объекту. Этот шаблон встречается в функциях и декораторах более высокого порядка. Они действительны только при использовании в Concatenate, или в качестве первого аргумента для Callable, или в качестве параметров для определяемых пользователем универсальных шаблонов Generic

Теперь, чтобы добавить базовое ведение журнала в функцию, можно создать декоратор @add_logging() для ведения журнала вызовов функций. Переменная спецификации параметра сообщает средству проверки типов, что вызываемый объект, переданный в декоратор, и новый вызываемый объект, возвращаемый им, имеют взаимозависимые параметры типа:

from collections.abc import Callable
import logging

def add_logging[T, **P](f: Callable[P, T]) -> Callable[P, T]:
    '''Типобезопасный декоратор для добавления логирования к функции.'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> T:
        logging.info(f'{f.__name__} was called')
        return f(*args, **kwargs)
    return inner

@add_logging
def add_two(x: float, y: float) -> float:
    '''Сложение двух чисел.'''
    return x + y

Без ParamSpec самым простым способом аннотировать такой шаблон раньше, нужно было использование TypeVar с привязкой Callable[..., Any]. Что вызывает две проблемы:

  • Средство проверки типов не может проверить тип внутренней функции, т.к. для *args и **kwargs необходимо ввести Any.
  • В теле декоратора @add_logging() при возврате внутренней функции может потребоваться cast(), или необходимо указать средству проверки статического типа игнорировать возвращаемый внутренний результат.

ParamSpec.args:
ParamSpec.kwargs:

Так как ParamSpec фиксирует как позиционные, так и ключевые аргументы, ParamSpec.args и ParamSpec.kwargs могут использоваться для разделения ParamSpec на его компоненты.

  • ParamSpec.args представляет кортеж позиционных параметров в данном вызове и должен использоваться только для аннотации *args.
  • ParamSpec.kwargs представляет собой сопоставление ключевых аргументов с их значениями в данном вызове и должен использоваться только для аннотирования **kwargs.

Оба атрибута требуют, чтобы аннотированный параметр находился в области действия. Во время выполнения ParamSpec.args и ParamSpec.kwargs являются экземплярами ParamSpecArgs и ParamSpecKwargs соответственно.

ParamSpec.__name__:

Атрибут ParamSpec.__name__ представляет собой название спецификации параметра.

Переменные спецификации параметров, созданные с помощью covariant=True или contravariant=True, можно использовать для объявления ковариантных или контравариантных универсальных типов. Также допускается связанный аргумент bound, аналогичный TypeVar. Однако фактическая семантика этих ключевых слов еще не определена.

Пример использования в версиях Python <= 3.11.

Переменная спецификации параметра сообщает средству проверки типов, что вызываемый объект, переданный в декоратор, и новый вызываемый объект, возвращаемый им, имеют взаимозависимые параметры типа:

from collections.abc import Callable
from typing import TypeVar, ParamSpec
import logging

T = TypeVar('T')
P = ParamSpec('P')

def add_logging(f: Callable[P, T]) -> Callable[P, T]:
    '''Типобезопасный декоратор для добавления логирования к функции.'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> T:
        logging.info(f'{f.__name__} был вызван')
        return f(*args, **kwargs)
    return inner

@add_logging
def add_two(x: float, y: float) -> float:
    '''Сложение двух чисел.'''
    return x + y

Новое в Python 3.10.