假设我们有以下Python 3代码:
from typing import Callable, Concatenate, ParamSpec, TypeAlias
class BaseContext:
...
Params = ParamSpec("Params")
def reset_command_context(
_func: Callable[Concatenate[BaseContext, Params], None],
*,
current: bool = False,
) -> None:
...
@reset_command_context
def process_invite_unique(ctx: BaseContext, unique: bool) -> None:
...
@reset_command_context
def process_ban(ctx: BaseContext) -> None:
...
然后运行静态类型检查器:
~ $ pyright test.py
WARNING: there is a new pyright version available (v1.1.330 -> v1.1.330.post0).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`
0 errors, 0 warnings, 0 informations
~ $ mypy test.py
Success: no issues found in 1 source file
看上去我们都没事。但是让我们为Callable
定义TypeAlias
,然后使用该别名在reset_command_context
装饰器中注解_func
参数:
FuncSpec: TypeAlias = Callable[Concatenate[BaseContext, Params], None]
def reset_command_context(
_func: FuncSpec,
*,
current: bool = False,
) -> None:
...
运行静态类型检查器现在显示不同的结果。pyright
仍然可以,但mypy
归咎于:
~ $ mypy test.py
test2.py:20: error: Argument 1 to "reset_command_context" has incompatible type "Callable[[BaseContext, bool], None]"; expected "Callable[[BaseContext, VarArg(Any), KwArg(Any)], None]" [arg-type]
test2.py:20: note: This is likely because "process_invite_unique" has named arguments: "ctx". Consider marking them positional-only
test2.py:25: error: Argument 1 to "reset_command_context" has incompatible type "Callable[[BaseContext], None]"; expected "Callable[[BaseContext, VarArg(Any), KwArg(Any)], None]" [arg-type]
test2.py:25: note: This is likely because "process_ban" has named arguments: "ctx". Consider marking them positional-only
Found 2 errors in 1 file (checked 1 source file)
这是mypy
中的一个bug还是我做错了什么?
1条答案
按热度按时间disbfnqx1#
FuncSpec
是泛型类型别名,因为在其定义中使用了类型变量(ParamSpec
)。无论你在哪里使用
FuncSpec
,你都可以用一个参数规范来参数化它--具体的一个或另一个ParamSpec
。比如说,FuncSpec[[int, str, float]]
,FuncSpec[...]
,或FuncSpec[Params]
。如果没有显式地将其参数化,类型检查器将把它视为
FuncSpec[Any]
。在这种情况下,我拒绝你的代码是正确的。您提示
reset_command_context
接受FuncSpec[Any]
,它是可调用的,允许使用BaseContext
位置参数和 * 任何数量的附加位置和关键字参数 * 进行调用。process_invite_unique
和process_ban
都不能绑定该签名,因为它们不能接受任意多的参数。您需要为它们提供ctx: BaseContext, *args: Any, **kwargs: Any
签名,以便它们按照所编写的那样使用reset_command_context
(实际上,如果您进行了更改,它们就会这样做)。看起来你真正想要的是两件事之一:
1.**你希望
FuncSpec
表示一个接受BaseContext
和任意数量的附加参数的可调用对象。**你不关心专门化它或“捕获”这些附加参数的参数规范。如果是这样的话,你需要的是
FuncSpec[...]
:这将通过编写的类型检查,但将给予
process_invite_unique
和process_ban
类型None
,这可能不是您想要的。1.您希望“捕获”
_func
的参数规格,并在reset_command_context
的签名中重复使用。假设您希望
reset_command_context
返回一个可调用对象,尾部参数指定为_func
,BaseContext
从前面弹出。可以使用现有的Params
类型变量表示为这将给予
process_invite_unique
类型Callable[[bool], None
和process_ban
类型Callable[[], None]
。您可以使用reveal_type
验证这一点:这个produces the mypy output: