如何让 python 键入在期望其父 class 时将子 class 识别为有效类型?
How to make python typing recognize subclasses as valid types when it expects their parent class?
这是我需要做的最简单的例子:
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
函数尽可能通用。现在这给出了与上面相同的警告。
参数子类型与 return 子类型相反。
- Return 值从被调用方分配给调用方。
- 参数值从调用者分配给被调用者。
并且赋值应该比变量的预期类型更具体。
例如:
data: Data = SpecificData() # okay
data: SpecificData = Data() # not okay
所以你应该这样做:
from typing import Callable, Any
class Data:
pass
class SpecificData(Data):
pass
class Event:
pass
class SpecificEvent(Event):
pass
def detect_specific_event(data: Data, other_info: str) -> SpecificEvent:
return SpecificEvent()
def run_detection(callback: Callable[[SpecificData, Any], Event]) -> None:
return
run_detection(detect_specific_event)
我花了一段时间才记住要使用哪个打字,但恕我直言,你想使用 cast
与在其他语言中的使用不同,cast(x,y) 不会 做 任何事情,但它确实告诉输入 将 y 视为类型 x。运行时,它是一个空操作,只是 returns y.
像编译型语言,如果我读它,我会特别注意代码:它真的可以在运行时运行吗?数据类型实际上是正确的吗?:
- 如果你不能保证以后生成数据的任何东西只会分发
SpecificData
s,那么带有 LSP 注释的重复闭包是合适的。如果可以,那么铸造就可以了。你的最小示例缺少那一点,但如果你显示了实际数据正在通过 print(data)
那么我们就会知道是否应用了 LSP。
from typing import Callable, Any, cast
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(cast((Callable[[Data, Any], Event]),detect_specific_event))
在这里,您基本上已经告诉打字,"accept my word for it" detect_specific_event
是 Callable[[Data, Any], Event])
。
运行和类型检查的输出:
$ mypy test2.py
Success: no issues found in 1 source file
$ python test2.py
(venv)$ well your code says nothing.
将演员表更改为实际签名:
run_detection(cast((Callable[[SpecificData, Any], SpecificEvent]),detect_specific_event))
(venv) $@so.mypy$ mypy test2.py
Argument 1 to "run_detection" has incompatible type "Callable[[SpecificData, Any], SpecificEvent]"; expected "Callable[[Data, Any], Event]"
Found 1 error in 1 file (checked 1 source file)
$ python test2.py
$ well your code says nothing.
这是我需要做的最简单的例子:
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
函数尽可能通用。现在这给出了与上面相同的警告。
参数子类型与 return 子类型相反。
- Return 值从被调用方分配给调用方。
- 参数值从调用者分配给被调用者。
并且赋值应该比变量的预期类型更具体。 例如:
data: Data = SpecificData() # okay
data: SpecificData = Data() # not okay
所以你应该这样做:
from typing import Callable, Any
class Data:
pass
class SpecificData(Data):
pass
class Event:
pass
class SpecificEvent(Event):
pass
def detect_specific_event(data: Data, other_info: str) -> SpecificEvent:
return SpecificEvent()
def run_detection(callback: Callable[[SpecificData, Any], Event]) -> None:
return
run_detection(detect_specific_event)
我花了一段时间才记住要使用哪个打字,但恕我直言,你想使用 cast
与在其他语言中的使用不同,cast(x,y) 不会 做 任何事情,但它确实告诉输入 将 y 视为类型 x。运行时,它是一个空操作,只是 returns y.
像编译型语言,如果我读它,我会特别注意代码:它真的可以在运行时运行吗?数据类型实际上是正确的吗?:
- 如果你不能保证以后生成数据的任何东西只会分发
SpecificData
s,那么带有 LSP 注释的重复闭包是合适的。如果可以,那么铸造就可以了。你的最小示例缺少那一点,但如果你显示了实际数据正在通过print(data)
那么我们就会知道是否应用了 LSP。
from typing import Callable, Any, cast
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(cast((Callable[[Data, Any], Event]),detect_specific_event))
在这里,您基本上已经告诉打字,"accept my word for it" detect_specific_event
是 Callable[[Data, Any], Event])
。
运行和类型检查的输出:
$ mypy test2.py
Success: no issues found in 1 source file
$ python test2.py
(venv)$ well your code says nothing.
将演员表更改为实际签名:
run_detection(cast((Callable[[SpecificData, Any], SpecificEvent]),detect_specific_event))
(venv) $@so.mypy$ mypy test2.py
Argument 1 to "run_detection" has incompatible type "Callable[[SpecificData, Any], SpecificEvent]"; expected "Callable[[Data, Any], Event]"
Found 1 error in 1 file (checked 1 source file)
$ python test2.py
$ well your code says nothing.