python - Как записать тип функции с переменным количеством аргументов?


0

Например, есть функция, которую нужно сохранить в переменной:

def foo(*args: int) -> None:
    pass

# stored_function: ? = foo

Как правильно записать тип переменной?


Вариант ниже выдаёт TypeError во время исполнения:

stored_function: Callable[[*int], None] = foo

PyCharm (2018.3.4 CE) выводит следующий тип для этой функции:

(args: Tuple[int, ...]) -> None

Его можно транслировать в

Callable[[Tuple[int, ...]], None]

Но выяснилось, что это не является официальным стилем и подобный тип не пройдёт проверку даже в самом PyCharm:

def boo(func: Callable[[Tuple[int, ...]], None]) -> None:
    func(1, 2, 3)
#           ^  ^ Unexpected argument

При этом вызов boo(foo) предупреждение не генерирует.

Источник
  •  895
  •  1
  • 22 янв 2019 2019-01-22 22:52:09

1 ответ

1

Здесь важно понимать цель добавления аннотации типа.

Если это просто подсказка читающему программисту, то можно использовать любой стиль, принятый в команде. Аннотации в таком случае достаточно исполняться без ошибок (а если добавить строку from __future__ import annotations, то подойдёт любая синтаксически верная конструкция).

Если же хочется иметь работающую статическую проверку типов, то здесь ситуация более сложная.

Официальной поддержки таких случаев нет.
PEP-484:

Since using callbacks with keyword arguments is not perceived as a common use case, there is currently no support for specifying keyword arguments with Callable. Similarly, there is no support for specifying callback signatures with a variable number of argument of a specific type.

Свободный перевод:

Так как использование функторов с именованными аргументами не видится частым случаем, в текущем виде нет поддержки определения именованных аргументов для Callable. Аналогично нет поддержки указания сигнатуры функторов с переменным количеством аргументов определённого типа.

PEP-483:

Callable[[t1, t2, ..., tn], tr]. [...] There is no way to indicate optional or keyword arguments, nor varargs, [...].

Примерный перевод:

Callable[[t1, t2, ..., tn], tr]. [...] Нет способа обозначить необязательные или именованные аргументы, а также переменное количество аргументов, [...].

В документации к модулю typing предлагается использовать следующий тип:

stored_function: Callable[..., None] = foo

В таком случае допускаются произвольные аргументы, но на возвращаемое значение всё равно накладывается ограничение.


Если есть возможность использовать сторонние инструменты, то mypy содержит экспериментальную возможность обработки такого случая (а также более сложных вариантов):

from typing import Callable
from mypy_extensions import VarArg

# def foo...

stored_function: Callable[[VarArg(int)], None] = foo