python-3.x mypy错误时,别名Callable与类型

h79rfbju  于 2023-10-21  发布在  Python
关注(0)|答案(1)|浏览(162)

假设我们有以下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还是我做错了什么?

disbfnqx

disbfnqx1#

FuncSpec是泛型类型别名,因为在其定义中使用了类型变量(ParamSpec)。
无论你在哪里使用FuncSpec,你都可以用一个参数规范来参数化它--具体的一个或另一个ParamSpec。比如说,

  • FuncSpec[[int, str, float]]
  • FuncSpec[...],或
  • FuncSpec[Params]

如果没有显式地将其参数化,类型检查器将把它视为FuncSpec[Any]
在这种情况下,我拒绝你的代码是正确的。您提示reset_command_context接受FuncSpec[Any],它是可调用的,允许使用BaseContext位置参数和 * 任何数量的附加位置和关键字参数 * 进行调用。process_invite_uniqueprocess_ban都不能绑定该签名,因为它们不能接受任意多的参数。您需要为它们提供ctx: BaseContext, *args: Any, **kwargs: Any签名,以便它们按照所编写的那样使用reset_command_context(实际上,如果您进行了更改,它们就会这样做)。
看起来你真正想要的是两件事之一:
1.**你希望FuncSpec表示一个接受BaseContext和任意数量的附加参数的可调用对象。**你不关心专门化它或“捕获”这些附加参数的参数规范。
如果是这样的话,你需要的是FuncSpec[...]

def reset_command_context(
    _func: FuncSpec[...],
    *,
    current: bool = False,
) -> None:

这将通过编写的类型检查,但将给予process_invite_uniqueprocess_ban类型None,这可能不是您想要的。
1.您希望“捕获”_func的参数规格,并在reset_command_context的签名中重复使用。
假设您希望reset_command_context返回一个可调用对象,尾部参数指定为_funcBaseContext从前面弹出。可以使用现有的Params类型变量表示为

def reset_command_context(
    _func: FuncSpec[Params],
    *,
    current: bool = False,
) -> Callable[Params, None]:

这将给予process_invite_unique类型Callable[[bool], Noneprocess_ban类型Callable[[], None]。您可以使用reveal_type验证这一点:

@reset_command_context
def process_invite_unique(ctx: BaseContext,  unique: bool) -> None:
    ...

@reset_command_context
def process_ban(ctx: BaseContext) -> None:
    ...

reveal_type(process_invite_unique)
reveal_type(process_ban)

这个produces the mypy output

main.py:32: note: Revealed type is "def (unique: builtins.bool)"
main.py:33: note: Revealed type is "def ()"
Success: no issues found in 1 source file

相关问题