我有一个类,它可能包含一个将在其他地方调用的函数。根据调用它的内容,它可以有可变数量的参数。一个所有者可能使用参数(event, int, str)
调用它,另一个所有者可能使用(event, str, bool, dict)
调用它。稍后会进行验证,以确保签名与所有者所需的签名相匹配。出于类型提示的目的,我需要确保传递的签名匹配以Event
对象开始的任何内容,以及fine和dandy之后的任何内容。因此,像def foo(event: Event, a: int, b: bool)
和def (event: ClickEvent, c: dict, *args, **kwargs) -> typing.Coroutine[Any, Any, str]
这样的函数都绝对有效。
给出下面的例子:
from typing import *
from dataclasses import dataclass
PARAMS = ParamSpec("PARAMS")
@dataclass
class Event:
field1: int
field2: bool
name: str
def get_name(self) -> str:
return self.name
class SomeCaller:
...
class Element:
...
class ClickEvent(Event):
def __init__(self, field1: int, field2: int, target: str):
super().__init__(field1=field1, field2=field2, name="click")
self.__target = target
@property
def target(self) -> str:
return self.__target
# The type hint in question
HANDLER = Callable[
[
Event,
PARAMS
], Union[Any, Coroutine]
]
def control(event: Event, *args, **kwargs) -> bool:
pass
def false_control(event: ClickEvent, *args, **kwargs) -> bool:
pass
async def async_function(event: Event, arg1: int, arg2: int, *args, **kwargs):
return 9
def function(event: ClickEvent, arg1: int, arg2: int, *args, **kwargs):
return 7
def other_function(event: Event, caller: SomeCaller, element: Element):
return 8
class EventHandlerWhatsit:
def __init__(self, handler: HANDLER):
self.__handler = handler
control_value = EventHandlerWhatsit(control)
false_control_value = EventHandlerWhatsit(false_control)
async_function_value = EventHandlerWhatsit(async_function)
function_value = EventHandlerWhatsit(function)
other_function_value = EventHandlerWhatsit(other_function)
def main():
print("This works")
if __name__ == "__main__":
main()
字符串
在false_control_value
、async_function_value
、function_value
和other_function_value
的声明中会出现键入提示警告,所有这些都带有类似Expected type '(Event, ParamSpec("PARAMS")) -> Coroutine | Any' (matched generic type '(Event, ParamSpec("PARAMS")) -> Coroutine | Any'), got '(event: Event, arg1: int, arg2: int, args: tuple[Any, ...], kwargs: dict[str, Any]) -> Any' instead
的警告。control_value
的声明没有问题。在EventHandlerWhatsit
的初始化器中对self.__handler = handler
的赋值也显示了Expected type '(Event, ParamSpec("PARAMS")) -> Coroutine | Any', got '(Event, ParamSpec("PARAMS")) -> Coroutine | Any' instead
的警告,我觉得这很奇怪。
它只需要指出“只要以Event
的子类作为参数开始,就可以调用的东西”。名字不重要。我已经用各种方式尝试了HANDLER
的定义,例如将*args
和**kwargs
定义为Tuple[Any, ...]
和Dict[str, Any]
(有和没有Optional
),使用额外的参数,但最终仍然得到相同类型的警告。
我被python 3.8卡住了,我在PyCharm中编辑,它显示了警告。
有什么想法吗
编辑:
@Daniil Fajnberg和@SUTerliakov在评论中提供了完美的答案:
HANDLER = Callable[
Concatenate[
Event,
PARAMS
], Union[Any, Coroutine]
]
型Concatenate
允许注解匹配与输入定义中稍有不同的值。
下面的代码是无效的:
def test_inner(arg: Callable[[str, int, P], Any]):
pass
def test_input(i: str, j: int, q: str = None, *args, **kwargs):
pass
def test_outer():
test_inner(test_input)
型
linter将触发警告,因为可选的q
参数的存在不符合test_inner
中arg
参数的预期。但是,将test_inner
中的arg
的定义更改为arg: Callable[Concatenate[str, int, P], Any]
,linter就可以了。
警告:ParamSpec
和Concatenate
是在Python 3.10中引入的。如果由于环境限制而需要使用旧版本,请使用typing_extensions
包来提供该功能。
1条答案
按热度按时间mkh04yzy1#
@Daniil Fajnberg和@SUTerliakov在评论中提供了完美的答案:
字符串
Concatenate
允许注解匹配与输入定义中稍有不同的值。下面的代码是无效的:
型
linter将触发警告,因为可选
q
参数的存在不符合test_inner
中arg
参数的预期。但是,将test_inner
中的arg
的定义更改为arg: Callable[Concatenate[str, int, P], Any]
,linter就可以了。警告:
ParamSpec
和Concatenate
是在Python 3.10中引入的。如果由于环境限制而需要使用旧版本,请使用typing_extensions
包来提供该功能。