如何将自定义协议与 Callable 协议结合起来? [英] How to combine a custom protocol with the Callable protocol?
问题描述
我有一个装饰器,它接受一个函数并返回具有一些附加属性的相同函数:
I have a decorator that takes a function and returns the same function with some added attributes:
import functools
from typing import *
def decorator(func: Callable) -> Callable:
func.attr1 = "spam"
func.attr2 = "eggs"
return func
如何键入提示decorator
的返回值?我希望类型提示传达两条信息:
How do I type hint the return value of decorator
? I want the type hint to convey two pieces of information:
- 返回值是一个
Callable
- 返回值具有属性
attr1
和attr2
如果我写一个协议,
class CallableWithAttrs(Protocol):
attr1: str
attr2: str
然后我失去了Callable
.显然我不能让协议继承自 Callable
;
then I lose Callable
. And apparently I can't make the protocol inherit from Callable
;
class CallableWithAttrs(Callable, Protocol):
attr1: str
attr2: str
mypy
说:
error: Invalid base class "Callable"
另一方面,如果我只使用Callable
,我会丢失有关添加属性的信息.
On the other hand, if I just use Callable
, I lose the information about the added attributes.
这在引入类型变量时可能更加复杂,即当装饰器必须返回与给定函数 func
相同类型的可调用对象时,正如 MisterMiyagi 在评论中指出的那样.
This is perhaps even more complicated when introducing type variables, i.e. when the decorator must return the same type of callable as the given function func
, as pointed out by MisterMiyagi in the comments.
import functools
from typing import *
C = TypeVar('C', bound=Callable)
def decorator(func: C) -> C:
func.attr1 = "spam"
func.attr2 = "eggs"
return func
现在我该怎么办?我不能从类型变量继承:
Now what do I do? I can't inherit from a type variable:
class CallableWithAttrs(C, Protocol):
attr1: str
attr2: str
error: Invalid base class "C"
推荐答案
可以通过Callable
参数化一个Protocol
:
from typing import Callable, TypeVar, Protocol
C = TypeVar('C', bound=Callable) # placeholder for any Callable
class CallableObj(Protocol[C]): # Protocol is parameterised by Callable C ...
attr1: str
attr2: str
__call__: C # ... which defines the signature of the protocol
这会创建Protocol
本身与任意Callable
的交集.
This creates an intersection of the Protocol
itself with an arbitrary Callable
.
接受任何可调用 C
的函数因此可以返回 CallableObj[C]
,一个具有所需属性的相同签名的可调用对象:
A function that takes any callable C
can thus return CallableObj[C]
, a callable of the same signature with the desired attributes:
def decorator(func: C) -> CallableObj[C]: ...
MyPy 正确识别签名和属性:
MyPy properly recognizes both the signature and attributes:
def dummy(arg: str) -> int: ...
reveal_type(decorator(dummy)) # CallableObj[def (arg: builtins.str) -> builtins.int]'
reveal_type(decorator(dummy)('Hello')) # int
reveal_type(decorator(dummy).attr1) # str
decorator(dummy)(b'Fail') # error: Argument 1 to "dummy" has incompatible type "bytes"; expected "str"
decorator(dummy).attr3 # error: "CallableObj[Callable[[str], int]]" has no attribute "attr3"; maybe "attr2"?
这篇关于如何将自定义协议与 Callable 协议结合起来?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!