是否可以在Lambda函数中输入提示?

目前,在Python中,函数的参数和返回类型可以按如下方式进行类型提示:

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

它指示该函数接受两个字符串并返回一个整数。

但是,这种语法与lambdas非常容易混淆,lambdas看起来像:

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

我试着在参数和返回类型上都加入类型提示,但我找不到一种不会导致语法错误的方法。

是否可以在Lambda函数中输入提示?如果没有,是否有类型提示lambdas的计划,或者任何原因(除了明显的语法冲突)为什么没有?

93221 次浏览

在Python3.6及更高版本中,您可以使用PEP 526变量注释。您可以使用typing.Callable通用来注释将lambda结果分配给的变量:

from typing import Callable


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

这不会将类型提示信息附加到函数对象本身,只会附加到存储对象的名称空间,但这通常是类型提示所需的全部内容。

但是,您也可以只使用函数语句。lambda提供的唯一优点是,您可以将简单表达式的函数定义__更大的表达式ABC2。但是上面的lambda不是更大的表达式的一部分,它只是赋值语句的一部分,将其绑定到一个名称。这正是def func(var1: str, var2: str): return var1.index(var2)语句所要实现的。

请注意,您也不能单独注释*args**kwargs参数,正如Callable的文档所述:

没有指示可选或关键字参数的语法;此类函数类型很少用作回调类型。

该限制不适用于PEP 544方案采用__call__方法;如果您需要一个关于应该接受哪些参数的表达性定义,请使用它。您需要Python3.8__abc3为backport安装__abc4:

from typing_extensions import Protocol


class SomeCallableConvention(Protocol):
def __call__(self, var1: str, var2: str, spam: str = "ham") -> int:
...


func: SomeCallableConvention = lambda var1, var2, spam="ham": var1.index(var2) * spam

对于lambda表达式本身,您不能使用任何注释(Python的类型提示所基于的语法)。该语法仅适用于def函数语句。

PEP 3107-函数注释

Lambda的语法不支持注释。通过要求在参数列表周围使用括号,可以更改lambda的语法以支持注释。但是,已经决定了不进行此更改,因为:

  • 这将是一个不相容的变化。
  • Lambda已经被阉割了。
  • Lambda总是可以更改为函数。

您仍然可以将注释直接附加到对象,__abc0属性是一个可写的字典:

>>> 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'>}

当然,当您想要在类型提示上运行静态分析器时,像这样的动态注释并不会对您有所帮助。

从Python 3.6开始,您可以(请参阅PEP 526):

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

对于那些只想在编写代码时快速访问IntelliSense的人来说,几乎可以__abc0,完成您的工作,然后__abc1使用参数隐藏它。

    x: YourClass
map(lambda _ :  x.somemethod ...) # x has access to methods defined on YourClass

然后,紧接着:

    x: YourClass # can remove or leave
map(lambda x:  x.somemethod, ListOfYourObjects) # inner x now shadows the argument

这对我来说很好……

def fun()->None:
def lam(i: int)->None:
print("lam!", i)
print("fun!")
lam(1)
lam(2)




fun()
lam()

指纹

fun!
lam! 1
lam! 2
Traceback (most recent call last):
File "/home/jail/prog.py", line 16, in <module>
lam()
NameError: name 'lam' is not defined

在CPython 3.6.12和3.10.2上测试

我非常不喜欢将lambda函数分配给标识符(请参阅是Python的吗:命名Lambdas)。尽管如此,如果你想这样做,你在这里已经有了答案。

如果您不这样做,并且只想让类型检查器做它们的事情,那么答案是typing.cast

from typing import cast, Callable


cast(Callable[[int], int], lambda x: x + 1)("foo")
# error: Argument 1 has incompatible type "str"; expected "int"