functools.partial
的正确类型提示是什么?我有一个返回 partial
的函数,我想输入提示,以便 mypy
不会抛出任何错误:
def my_func() -> ?:
return partial(foo, bar="blah")
比typing.Callable
更具体
答案 0 :(得分:-1)
您有多种选择,具体取决于您的目的。
例如,我将假设 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", [12,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, thud=1.5) # Works fine, 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, bar: str, thud: float = 0.5) -> str
# Does whatever
return "Hi"
再一次,我们无法用 Callable
精确处理这个问题,但 Protocol
就好了。我们只是确保 PartFoo
协议也指定了它。
class PartFoo(Protocol):
def __call__(fakeSelf, qux: int, *, thud: float=...) -> str:
...
现在我们的行为是
my_func()(7, thud=1.5) # Works fine, qux is passed positionally, thud is a kwarg
my_func()(qux=7, thud=1.5) # Works fine, 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
。