Можно ли набрать подсказку лямбда-функции?

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

def func(var1: str, var2: str) -> int:
    return var1.index(var2)

Это означает, что функция принимает две строки и возвращает целое число.

Однако этот синтаксис очень запутан с lambdas, которые выглядят так:

func = lambda var1, var2: var1.index(var2)

Я попытался ввести типы подсказок для обоих параметров и типов возвращаемых данных, и я не могу понять способ, который не вызывает синтаксическую ошибку.

Можно ли набрать подсказку лямбда-функции? Если нет, существуют ли планы для типа, указывающего lambdas, или по какой-либо причине (помимо очевидного конфликта синтаксиса), почему бы и нет?

Ответ 1

В Python 3.6 и более поздних версиях вы можете использовать аннотации переменных PEP 526. Вы можете аннотировать переменную, которой вы назначаете lambda результат, с typing.Callable generic typing.Callable:

from typing import Callable

func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)

При этом информация о подсказках типа не прикрепляется к самому объекту функции, а только к пространству имен, в котором вы сохранили объект, но обычно это все, что вам нужно для целей подсказки типов. Обратите внимание, что вы не можете аннотировать *args или **kwargs отдельно таким образом, так как документация для Callable гласит:

Нет синтаксиса для указания необязательных или ключевых аргументов; такие типы функций редко используются в качестве типов обратного вызова.

Для самого lambda выражения нельзя использовать какие-либо аннотации (синтаксис, на котором построен хинтинг типа Python). Синтаксис доступен только для операторов функции def.

От PEP 3107 - аннотации функций:

Лямбда-синтаксис не поддерживает аннотации. Синтаксис лямбда может быть изменен для поддержки аннотаций, требуя скобки вокруг списка параметров. Однако было решено не вносить это изменение, потому что:

  • Это было бы несовместимым изменением.
  • Лямбда в любом случае кастрирована.
  • Лямбда всегда можно изменить на функцию.

Вы все еще можете прикрепить аннотации непосредственно к объекту, атрибут function.__annotations__ - это доступный для записи словарь:

>>> def func(var1: str, var2: str) -> int:
...     return var1.index(var2)
...
>>> func.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
>>> lfunc = lambda var1, var2: var1.index(var2)
>>> lfunc.__annotations__
{}
>>> lfunc.__annotations__['var1'] = str
>>> lfunc.__annotations__['var2'] = str
>>> lfunc.__annotations__['return'] = int
>>> lfunc.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}

Конечно, не такие динамические аннотации помогут вам, когда вы захотите запустить статический анализатор над подсказками типов.

Ответ 2

Начиная с Python 3.6, вы можете (см. PEP 526):

from typing import Callable
is_even: Callable[[int], bool] = lambda x: (x % 2 == 0)

Как заметил пользователь cz, это не то же самое, что аннотировать подпись неанонимной функции. Mypy v0.620 не будет жаловаться, если вы передадите переменную str в is_even в приведенном выше примере.