mypy 似乎认为 (*args, **kwargs) 可以匹配任何函数签名?

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

mypy 如何将 Liskov 替换原则应用于

*args, **kwargs
参数?

我认为以下代码应该无法通过 mypy 检查,因为

f
类允许对
Base
的某些调用不被
C
允许,但它实际上通过了。这有什么原因吗?

from abc import ABC, abstractmethod
from typing import Any


class Base(ABC):
    @abstractmethod
    def f(self, *args: Any, **kwargs: Any) -> int:
        pass


class C(Base):
    def f(self, batch: int, train: bool) -> int:
        return 1

我也尝试删除

*args
**kwargs
,但都失败了。

python python-typing mypy liskov-substitution-principle
2个回答
3
投票

与目前接受的答案中所说的Daniil不同,原因正是

(*args: Any, **kwargs: Any)
签名部分。

请查看mypy

问题跟踪器
上的相应讨论:

我实际上很喜欢这个想法,我已经多次看到这种混乱,虽然它有点不安全,但大多数时候当人们写 (*args, **kwargs) 时,它意味着“不关心”,而不是“应该适用于所有呼叫”。

[GVR] 同意,这是实用性胜过纯粹性的情况。

所以,

mypy
对形式的功能进行了特殊处理

# _T is arbitrary type
class _:
    def _(self, *args, **kwargs) -> _T: ...

并认为它们完全等同于

Callable[..., _T]

是的,当然,这实际上违反了 LSP,但这是专门设计的,允许使用签名“忽略我的参数”来声明函数。

要声明真正接受任意位置和关键字参数的最广泛的函数,您应该在签名中使用

object


1
投票

这与

*args
**kwargs
本身无关。其原因完全是因为您对两个注释都使用了
typing.Any

Any
注释基本上是类型检查器的绝地思维技巧,其效果是:

这些您正在寻找的类型。

不管怎样,总会过去的。

因此,

typing
文档特别建议尽可能多地使用
object
来代替
Any
,当你想说“最广泛的可能类型”之类的东西时。当您遇到 Python 类型系统的限制时,Any
 应保留为最后的手段。

mypy

文档还有一个
部分解释了Any
object
之间的区别。

如果您将其中一个

Any

 注释更改为 
object
,您将受到 
mypy
 的正确惩罚,并出现 
[override]
C.f
 错误。

示例:

from typing import Any class Base: def f(self, *args: object, **kwargs: Any) -> int: return 2 class C(Base): def f(self, batch: int, train: bool) -> int: # now this is an error return 1
然而,“任意数量的位置和关键字参数”

与“每个参数将始终通过类型检查”的组合本质上翻译为“”没有覆盖永远是错误的“(就参数而言) ). 所以我建议使用 object

而不是

Any

 
everywhere
,除非你无法避免使用后者。
这些混乱是我认为选择命名这个构造的原因之一

Any

非常不幸。

PS

我的第一段措辞不好。正如

@SUTerliakov

更清楚地解释的那样,此覆盖不会导致错误的原因具体是因为

*args/**kwargs 参数的 combination

 以及它们用 
Any
 注释。只有当两个条件都满足时,
mypy
才会例外。
	

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