functools.partial 的正确类型提示

问题描述

functools.partial 的正确类型提示是什么?我有一个返回 partial函数,我想输入提示,以便 mypy 不会抛出任何错误

def my_func() -> ?:
    return partial(foo,bar="blah")

typing.Callable更具体

解决方法

您有多种选择,具体取决于您的目的。

例如,我将假设 foo 已定义

def foo(qux: int,thud: float,bar: str) -> str
    # Does whatever
    return "Hi"

如果我们使用 reveal_type,我们会发现 partial(foo,bar="blah") 被标识为 functools.partial[builtins.str*]。这大致转化为一个函数式的东西,它接受任何东西并返回一个字符串。因此,您可以准确地注释它,并且您至少可以在注释中获得返回类型。

def my_func() -> partial[str]:
    ...

a: str = my_func()(2,2.5) # Works fine
b: int = my_func()(2,2.5) # correctly fails,we know we don't get an int
c: str = my_func()("Hello",[12,13]) # Incorrectly passes. We don't know to reject those inputs.

我们可以更具体一些,这样在写函数的时候就多花点心思,让MyPy以后更好的帮助我们。一般来说,注释函数和类函数的东西有两个主要选项。有 Callable 和 Protocol。

Callable 通常更简洁,在处理位置参数时更有效。协议有点冗长,也适用于关键字参数。

因此,您可以将函数注释为

def my_func() -> Callable[[int,float],str]:

也就是说,它返回一个函数,该函数接受一个 int(对于 qux)和一个 float(对于 thud)并返回一个字符串。现在,请注意 MyPy 不知道输入类型将是什么,因此它无法验证位。 partial[str]Callable[[spam,ham,eggs],str] 一样兼容。但是,它确实通过而没有错误,如果您尝试将错误的参数传递给您的 Callable,它会向您发出警告。也就是说,

my_func()(7,2.6) # This will pass
my_func()("Hello",13]) # This will now correctly fail.

现在,让我们假设 foo 的定义如下。

def foo(qux: int,bar: str,thud: float) -> str
    # Does whatever
    return "Hi"

一旦我们获得了将 partial 作为关键字参数传递的 bar,就无法将 thud 作为位置参数传入。这意味着无法使用 Callable 来注释这个。相反,我们必须使用协议。

语法有点奇怪。它的工作原理如下。

class PartFoo(Protocol):
    def __call__(fakeSelf,qux: int,*,thud: float) -> str:
        ...

拆开 __call__ 行,我们首先有 fakeSelf 条目。这只是符号:如果调用是一个方法,第一个参数被吞下。

接下来,我们有 qux,和以前一样注释为 int。然后我们有 * 标记来指示后面的所有内容都只是关键字,因为我们无法再在实际方法中按位置到达 thud。然后我们有 thud 及其注释,最后我们有 -> str 来给出返回类型。

现在,如果您定义 def my_func() -> PartFoo:,您将获得我们想要的行为

my_func()(7,thud=1.5) # Works fine,qux is passed positionally,thud is a kwarg
my_func()(qux=7,both qux and thud are kwargs
my_func()(7) # Correctly fails because thud is missing
my_func()(7,1.5) # Correctly fails because thud can't be a positional arg.

您可能遇到的最后一种情况是您的原始方法具有可选参数。所以,让我们说

def foo(qux: int,thud: float = 0.5) -> str
    # Does whatever
    return "Hi"

再一次,我们无法用 Callable 精确处理这个问题,但 Protocol 就好了。我们只是确保 PartFoo 协议也指定了它。

class PartFoo(Protocol):
    def __call__(fakeSelf,thud: float=...) -> str:
        ...

现在我们的行为是

my_func()(7,both qux and thud are kwargs
my_func()(7) # Works fine because thud is optional
my_func()(7,1.5) # Correctly fails because thud can't be a positional arg.

总结一下,部分函数返回一个相当模糊的函数类型,您可以直接使用它,但会失去对输入的检查。您可以使用更具体的内容对其进行注释,在简单的情况下使用 Callable,在更复杂的情况下使用 Protocol

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...