1
0
不幸的是,你的第一个例子是合法不安全的——它违反了所谓的“里斯科夫替代原则”。
演示
为什么?
就是这样,让我简化一下您的示例:我将让基类接受任何类型的
从表面上看,这似乎很好。什么会出错? 好吧,假设我们写下面的片段。这段代码实际上检查的很好:派生的是基的子类,因此我们可以将派生的实例传递到接受基实例的任何程序中。同样地,base.fun可以接受任何对象,所以传入字符串肯定是安全的吗?
您可能会看到这是去哪里-这个程序实际上是不安全的,将在运行时崩溃!具体地说,最后一行被破坏了:我们传入了派生的和派生的
这就是为什么mypy禁止您缩小覆盖方法中参数类型的范围。如果派生是基的一个子类,这意味着我们应该能够 代替 派生的实例,在我们使用基的任何位置都不会破坏任何内容。这条规则特别被称为里斯科夫替代原理。 缩小参数类型可以防止这种情况发生。 (值得注意的是,Mypy要求你尊重Liskov这一事实实际上是相当标准的。几乎所有具有子类型的静态类型语言都做同样的事情——Java、C、C、C++…我所知道的唯一的反例就是艾菲尔。) 对于您的原始示例,我们可能会遇到类似的问题。为了使这一点更加明显,让我将您的一些类重命名为更现实一点。假设我们正在尝试编写某种类型的SQL执行引擎,并编写如下内容:
请注意,此代码与原始示例相同!唯一不同的是名字。 我们可以再次遇到类似的运行时问题——假设我们使用了类似这样的类:
如果允许对其进行类型检查,我们已经在代码中引入了一个潜在的安全漏洞!postgressqlExecutor只能接受我们明确决定标记为“已清理的sqlquery”类型的字符串的不变量已损坏。 现在,来回答你的另一个问题:为什么如果我们让BASE接受任何类型的论点,Mypy就不再抱怨了? 这是因为任何类型都有一个非常特殊的含义:它代表一个100%完全动态的类型。当你说“变量x的类型是any”时,你实际上是在说“我不希望你假定 任何东西 关于这个变量——我希望能够使用这个类型,不管你怎么抱怨!” 事实上,称任何“可能最广泛的类型”都是不准确的。实际上,它同时是最广泛的类型和最窄的类型。每个类型都是any的子类型,any是所有其他类型的子类型。Mypy将始终选择不会导致类型检查错误的任何站姿。 本质上,它是一个逃生舱,一种告诉类型检查器“我更了解”的方式。每当你给一个变量类型any时,实际上你完全不需要检查这个变量的类型,不管是好是坏。 有关更多信息,请参阅 typing.Any vs object? . 最后,你能做些什么? 不幸的是,我不确定是否有一个简单的方法可以解决这个问题:你必须重新设计你的代码。它从根本上说是不健全的,而且没有任何技巧可以保证让你摆脱困境。 你到底要怎么做取决于你到底想做什么。正如一位用户建议的那样,也许您可以对泛型做些什么。或者您可以按照另一个建议重命名其中一个方法。或者,您可以修改base.fun,使其使用与derived.fun相同的类型,或者反之亦然;您可以使derived不再从base继承。这完全取决于你具体情况的细节。 当然,如果情况真的 是 难处理的是,您可以完全放弃在该代码基的那个角进行类型检查,并使base.fun(…)接受任何(并接受您可能开始遇到运行时错误)。 必须考虑这些问题并重新设计代码似乎是一个不方便的麻烦——但是,我个人认为这是值得庆祝的事情!Mypy成功地阻止了您不小心在代码中引入了一个bug,并推动您编写更健壮的代码。 |
2
0
两个函数都有相同的名称,所以只需重命名其中1个函数即可。如果你这样做,Mypy会给你同样的错误:
将fun1重命名为fun1解决了mypy的问题,尽管它只是mypy的一个解决方法。
|
3
0
使用如下通用类:
其思想是用泛型类型定义一个基类。现在,您希望此泛型类型是
然后定义类型
当然,如果你定义的话,Mypy会抱怨的。
我在一篇评论中读到你想抛弃我。不要!相反,学习类型是如何工作的;当你在你觉得Mypy反对你的情况下,很可能有一些事情你不太明白。在这种情况下,寻求别人的帮助,而不是放弃! |
July · 如何定义数字间隔,然后四舍五入 1 年前 |
user026 · 如何根据特定窗口的平均值(行数)创建新列? 1 年前 |
Ashok Shrestha · 需要追踪特定的颜色线并获取坐标 1 年前 |
Nicote Ool · 在FastApi和Vue3中获得422 1 年前 |
Abdulaziz · 如何对集合内的列表进行排序[重复] 1 年前 |
asmgx · 为什么合并数据帧不能按照python中的预期方式工作 1 年前 |