我有一些代码可归结为以下内容:
from typing import Callable, TypeVar
T = TypeVar("T")
def default_fn() -> str:
return "Hello world!"
def call_fn(fn: Callable[[], T] = default_fn) -> T:
return fn()
注意,我使用的是 Python 3.10,因此我无法尝试新的 3.12 泛型语法。
问题的焦点是非常通用的
call_fn
函数,我希望使用通用类型提示来显示参数的返回类型与 call_fn
的返回类型相匹配。我还想要一个默认参数,这里是default_fn
。令我惊讶的是,Mypy(1.2.0,如果相关的话)对此抱怨:
$ mypy foo.py
foo.py:8: error: Incompatible default for argument "fn" (default has type "Callable[[], str]", argument has type "Callable[[], T]") [assignment]
Found 1 error in 1 file (checked 1 source file)
(第8行是
def call_fn...
线)
修复此错误(或修复此代码)的最“Pythonic”方法是什么?我遇到的修复都有某种缺点:
default_fn
的返回类型替换为 Any
这个选项并不是世界上最糟糕的事情,但据我了解,当你可以更精确时,
Any
应该避免使用,尤其是对于返回类型。
T
替换为 T | str
,或者无论默认函数的返回类型是什么这有点难看,而且读起来也很混乱(当你已经使用泛型类型时,为什么还需要指定类型?)
Optional[Callable[[], T]] = None
,并使用函数体将参数设置为default_fn
这不是可怕,还有其他原因使用此模式,例如当您希望默认值是可变的时。尽管如此,它比在通常的地方(即在函数签名中)使用默认函数更难阅读,这只是一种常见模式,因为在这些情况下它是必要。
我会选择第四个选项..使用
typing.overload
。您可以指定当没有参数时返回 str,否则它是参数上的通用函数。实际实现可以设置返回Any
.
from typing import Callable, TypeVar, overload, Any
T = TypeVar("T")
def default_fn() -> str:
return "Hello world!"
@overload
def call_fn() -> str:
...
@overload
def call_fn(fn: Callable[[], T]) -> T:
...
def call_fn(fn: Callable = default_fn) -> Any:
return fn()