目前,在Python中,函数的参数和返回类型可以被类型提示如下:

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

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

然而,这种语法与lambdas非常混淆,它看起来像:

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

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

是否有可能键入提示一个lambda函数?如果不是,是否有类型提示lambdas的计划,或者其他原因(除了明显的语法冲突)为什么不呢?


当前回答

这对我来说很好……

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上测试

其他回答

对于那些在编写代码时希望快速访问智能感知的人来说,一种近乎hack的方法是在lambda声明之前对参数进行类型注释,完成工作后才用参数遮蔽它。

    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"

从Python 3.6开始,你可以(参见PEP 526):

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

在Python 3.6及以上版本中,可以使用PEP 526变量注释。您可以用类型注释分配lambda结果的变量。可调用的通用:

from typing import Callable

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

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

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

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

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

该限制不适用于具有__call__方法的PEP 544协议;如果你需要一个有表现力的定义来说明什么论点应该被接受,可以使用这个。你需要Python 3.8或为一个backport安装typing-extensions项目:

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总是可以改变为一个函数。

您仍然可以将注释直接附加到对象(函数)。__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'>}

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