Pro*_*o Q 8 python type-hinting mypy
我有以下代码:
from typing import Callable
MyCallable = Callable[[object], int]
MyCallableSubclass = Callable[['MyObject'], int]
def get_id(obj: object) -> int:
return id(obj)
def get_id_subclass(obj: 'MyObject') -> int:
return id(obj)
def run_mycallable_function_on_object(obj: object, func: MyCallable) -> int:
return func(obj)
class MyObject(object):
'''Object that is a direct subclass of `object`'''
pass
my_object = MyObject()
# works just fine
run_mycallable_function_on_object(my_object, get_id)
# Does not work (it runs, but Mypy raises the following error:)
# Argument 2 to "run_mycallable_function_on_object" has incompatible type "Callable[[MyObject], int]"; expected "Callable[[object], int]"
run_mycallable_function_on_object(my_object, get_id_subclass)
Run Code Online (Sandbox Code Playgroud)
既然MyObject继承自object,为什么MyCallableSubclass在每个地方都MyCallable不起作用?
我已经阅读了一些关于Liskov 替换原则的内容,并且还查阅了有关协方差和逆变的Mypy 文档。然而,即使在文档本身中,他们也给出了一个非常相似的例子,他们说
Callable是在参数类型中表现逆变的类型示例,即Callable[[Employee], int]是 的子类型Callable[[Manager], int]。
那么为什么在 Mypy 中使用Callable[[MyObject], int]而不是Callable[[object], int]抛出错误呢?
总的来说,我有两个问题:
在我写这个问题的时候,我意识到了我的问题的答案,所以我想我仍然会问这个问题并回答它,以节省人们日后处理类似问题的时间。
这是怎么回事?
请注意 Mypy 文档中的最后一个示例:
Callable是在参数类型中表现逆变的类型示例,即Callable[[Employee], int]是 的子类型Callable[[Manager], int]。
在这里,Manager子类来自Employee. 也就是说,如果某事物期望一个可以接纳经理的功能,那么它可以过度概括并且可以接纳任何员工,因为它肯定会接纳经理。
然而,在我们的例子中,MyObject来自object. 因此,如果某些东西期待一个可以接收对象的函数,那么如果函数被过度指定并且只能接收MyObjects ,那就不好了。
为什么?想象一个名为的类NotMyObject,它继承自object,但不继承自MyObject。如果一个函数应该能够接受任何对象,那么它应该能够同时接受NotMyObjects 和MyObjects。但是,特定函数只能接收MyObjects,因此不适用于这种情况。
我该如何解决?
Mypy 是正确的。您需要将更具体的函数 ( MyCallableSubclass) 作为类型,否则您的代码可能有错误,或者您输入错误。