【发布时间】:2020-06-12 07:54:08
【问题描述】:
这是我需要做的一个最小示例:
from typing import Callable, Any
class Data:
pass
class SpecificData(Data):
pass
class Event:
pass
class SpecificEvent(Event):
pass
def detect_specific_event(data: SpecificData, other_info: str) -> SpecificEvent:
return SpecificEvent()
def run_detection(callback: Callable[[Data, Any], Event]) -> None:
return
run_detection(detect_specific_event)
现在我收到警告:
Expected type '(Data, Any) -> Event', got '(data: SpecificData, other_info: str) -> SpecificEvent' instead
对我来说,这个警告似乎没有意义,因为 SpecificData 和 SpecificEvent 分别是 Data 和 Event 的子类型,所以一切都应该没问题。有没有办法按照我的预期进行这项工作?我的想法是能够拥有类似的东西:
class OtherSpecificData(Data):
pass
class OtherSpecificEvent(Event):
pass
def detect_other_event(data: OtherSpecificData, other_info: str) -> OtherSpecificEvent:
return OtherSpecificEvent()
run_detection(detect_other_event)
所以run_detection 函数尽可能通用。现在这给出了与上面相同的警告。
【问题讨论】:
-
这里的问题是你在
run_detection中的参数类型意味着传递的可调用对象应该能够在Data及其所有子类上工作,但是你传递给它一个可调用对象,说它不能工作在Data上,它只能在SpecificData上工作。例如,假设detect_specific_event在SpecificData中使用了在父类中不可用的属性。但是run_detection被告知它应该期望的回调不会那样做;它被告知它将适用于Data及其所有子类。那么为什么它传递的函数需要SpecificData? -
您似乎希望父类充当其子类的联合,但它不能,因为子类可能具有父类没有的功能。如果
run_detection无论数据类型如何都可以调用回调,那么您要求函数比您想要的更具体(仅在基类上操作)(在任何子类上操作)。就run_detection而言,类之间的关系是无关紧要的。一个简单的解决方法是只使用您的子类型的Union。否则,请查找“组合优于继承”。 -
@Alkasm 我基本同意,但是......这里的 2 个函数可以工作,考虑到 LSP,如果进一步的数据,没有出现在这里都是特定的数据。但是 OP 的代码只显示连接
run_detection这是他们在这里询问的内容,而不是它如何获取数据。如果接下来只有一个SpecificData的生成器启动,那么它就可以工作了。但如果它发出Data事件则不会。 -
@JLPeyret 确实我的 cmets 对 OP 打算做什么做出了假设,这可能是真的,也可能不是。但是由于 OP 在最后声明“所以
run_detection尽可能通用”,所以这里要重点强调的是run_detection期望基类上的可调用对象是更多限制性的,而不是更笼统。
标签: python class typing callable subtype