协议实现者中的 Kwargs:什么是有效签名?

问题描述 投票:0回答:2

我的问题很简单。我有这个协议:

from typing import Protocol

class LauncherTemplateConfig(Protocol):
    def launch_program_cmd(self, **kwargs) -> list[str]:
        pass

这个协议的实现,我希望 mypy 通过,但它没有:

from typing import Optional
from pathlib import Path

class MyLauncherTemplateConfig:
    def launch_program_cmd(
        self, some_arg: Optional[Path] = None, another_arg=1
    ) -> list[str]:

我希望

MyLauncherTemplateConfig.launch_program_cmd
中的参数与协议类中的
**kwargs
兼容。

不确定我是否做错了什么...

python protocols python-typing mypy
2个回答
6
投票

一般原则

如果您希望 MyPy 接受某个类实现了

Protocol
中定义的接口,则具体实现中的相关方法必须在其接受的参数中不低于所定义的该方法的抽象版本。在Protocol
。这与面向对象编程的其他原则一致,例如
Liskov替换原则

具体问题在这里

您的

Protocol

 定义了一个接口,其中可以使用 
any
 关键字参数调用 
launch_program_cmd 方法,并且在运行时不会失败。您的具体实现不满足此接口,因为除 some_arg
another_arg
 之外的任何关键字参数都会导致该方法引发错误。

可能的解决方案

如果您希望 MyPy 将您的类声明为

Protocol

 的安全实现,您有两种选择。您可以将 
Protocol
 中的方法签名调整为更具体,也可以将具体实现中的方法签名调整为更通用。对于后者,你可以这样做:

from typing import Any, Protocol, Optional from pathlib import Path class LauncherTemplateConfig(Protocol): def launch_program_cmd(self, **kwargs: Any) -> list[str]: ... class MyLauncherTemplateConfig: def launch_program_cmd(self, **kwargs: Any) -> list[str]: some_arg: Optional[Path] = kwargs.get('some_arg') another_arg: int = kwargs.get('another_arg', 1) # and then the rest of your method
通过使用 

dict.get

 方法,我们可以在实现中保留默认值,但要坚持在 
Protocol
 中声明的方法的通用签名。
    


0
投票
关键问题是你的纵容颠倒了。从形式上来说,这是因为函数的输入是逆变的。

您对

def launch_program_cmd(self, **kwargs) -> list[str]:

的承诺是“此方法将能够采用任何一组关键字参数。”

举例来说,如果有人写

def launch_lunch(launcher: LauncherTemplateConfig): launcher.launch_program_cmd(food=["eggs", "spam", "spam"])
那么根据

LauncherTemplateConfig

的定义应该是允许的。

但是,如果您尝试使用

MyLauncherTemplateConfig

 的实例调用该方法,那么它会崩溃,因为它不知道如何处理 
food
 参数。所以 
MyLauncherTemplateConfig
 不是 
LauncherTemplateConfig
 的有效子类型

我怀疑你想要传达的意思更像是“这个方法将会存在,但我不知道它会采取什么论据。”然而,这并不是 MyPy 真正要表达的东西。基本原因是它不是很有用:对于一个方法将存在的承诺,您无能为力,但您不知道如何调用它!

(注意:相反的方向是允许的。如果您的协议指定您必须能够采用

some_arg

another_arg
 并且您的实现能够处理任何事情,那么这是允许的。但一般来说,您会希望您的协议能够指导您实际想要采取的措施。)

© www.soinside.com 2019 - 2024. All rights reserved.