如果我像这样定义我的装饰器
T = TypeVar('T', bound=Event)
def register1(evtype: Type[T]) -> Callable[[Callable[[T], None]], Callable[[T], None]]:
def decorator(handler):
# register handler for event type
return handler
return decorator
如果我在错误的函数上使用它,我会收到正确的错误:
class A(Event):
pass
class B(Event):
pass
@register1(A) # Argument of type "(ev: B) -> None" cannot be assigned to parameter of type "(A) -> None"
def handler1_1(ev: B):
pass
但是,如果我多次应用装饰器,则不起作用:
@register1(A) # Argument of type "(B) -> None" cannot be assigned to parameter of type "(A) -> None"
@register1(B)
def handler1_3(ev: A|B):
pass
我有点希望装饰器建立一个
Union
允许/必需的参数类型。
我认为
ParamSpec
是解决这个问题的方法,但是我如何使用 ParamSpec
既不覆盖参数类型,又要求参数类型与装饰器参数中的类型匹配?
使用
ParamSpec
不会导致任何类型错误:
P = ParamSpec("P")
def register2(evtype: Type[T]) -> Callable[[Callable[P, None]], Callable[P, None]]:
def decorator(handler):
# ...
return handler
return decorator
@register2(A) # This should be an error
def handler2_1(ev: B):
pass
如果我添加另一个
TypeVar
并使用Union
,它确实适用于双重装饰甚至三重装饰的函数,但不适用于单装饰的函数。
T2 = TypeVar('T2')
def register3(evtype: Type[T]) -> Callable[[Callable[[Union[T,T2]], None]], Callable[[Union[T,T2]], None]]:
def decorator(handler):
# ...
return handler
return decorator
# Expected error:
@register3(A) # Argument of type "(ev: B) -> None" cannot be assigned to parameter of type "(A | T2@register3) -> None"
def handler3_1(ev: B):
pass
# Wrong error:
@register3(A) # Argument of type "(ev: A) -> None" cannot be assigned to parameter of type "(A | T2@register3) -> None"
def handler3_2(ev: A):
pass
# Works fine
@register3(A)
@register3(B)
def handler3_3(ev: A|B):
pass
在写这个问题的时候,我离答案越来越近了。 我将在答案中提供我自己的解决方案。
但是,我很感兴趣是否有更好的方法来解决这个问题。
通过使用
Union
添加装饰函数仅接受装饰器参数的单个参数的情况,我不再从 pyright
收到任何意外错误:
def register4(evtype: Type[T]) -> Callable[[Union[Callable[[T|T2], None],Callable[[T], None]]], Callable[[T|T2], None]]:
def decorator(handler):
# ...
return handler
return decorator
#Expected errors
@register4(A) # Argument of type "(ev: B) -> None" cannot be assigned to parameter of type "((A | T2@register4) -> None) | ((A) -> None)"
def handler4_1(ev: B):
pass
@register4(A)
def handler4_2(ev: A):
pass
@register4(A)
@register4(B)
#@register4(C)
def handler4_3(ev: A|B|C):
pass