如何将自定义协议与 Callable 协议结合起来?
How to combine a custom protocol with the Callable protocol?
我有一个装饰器,它接受一个函数,return是具有一些附加属性的相同函数:
import functools
from typing import *
def decorator(func: Callable) -> Callable:
func.attr1 = "spam"
func.attr2 = "eggs"
return func
如何键入提示 decorator
的 return 值?我希望类型提示传达两条信息:
- return 值是
Callable
- return 值具有属性
attr1
和 attr2
如果我写一个协议,
class CallableWithAttrs(Protocol):
attr1: str
attr2: str
那我就输了Callable
。显然我不能让协议继承自 Callable
;
class CallableWithAttrs(Callable, Protocol):
attr1: str
attr2: str
mypy
说:
error: Invalid base class "Callable"
另一方面,如果我只使用 Callable
,我会丢失有关添加的属性的信息。
这在引入类型变量时可能更加复杂,即当装饰器必须 return 与给定函数 func
相同类型的可调用对象时,正如 MisterMiyagi 在评论中指出的那样。
import functools
from typing import *
C = TypeVar('C', bound=Callable)
def decorator(func: C) -> C:
func.attr1 = "spam"
func.attr2 = "eggs"
return func
现在我该怎么办?我无法从类型变量继承:
class CallableWithAttrs(C, Protocol):
attr1: str
attr2: str
error: Invalid base class "C"
自 typing.Callable
corresponds to collections.abc.Callable
以来,您只需定义一个 Protocol
实现 __call__
:
class CallableWithAttrs(Protocol):
attr1: str
attr2: str
def __call__(self, *args, **kwargs): pass
一个人可以参数化一个Protocol
一个Callable
:
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
的交集。
接受任何可调用函数 C
的函数因此可以 return CallableObj[C]
,具有相同签名且具有所需属性的可调用函数:
def decorator(func: C) -> CallableObj[C]: ...
MyPy 正确识别签名和属性:
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"?
我有一个装饰器,它接受一个函数,return是具有一些附加属性的相同函数:
import functools
from typing import *
def decorator(func: Callable) -> Callable:
func.attr1 = "spam"
func.attr2 = "eggs"
return func
如何键入提示 decorator
的 return 值?我希望类型提示传达两条信息:
- return 值是
Callable
- return 值具有属性
attr1
和attr2
如果我写一个协议,
class CallableWithAttrs(Protocol):
attr1: str
attr2: str
那我就输了Callable
。显然我不能让协议继承自 Callable
;
class CallableWithAttrs(Callable, Protocol):
attr1: str
attr2: str
mypy
说:
error: Invalid base class "Callable"
另一方面,如果我只使用 Callable
,我会丢失有关添加的属性的信息。
这在引入类型变量时可能更加复杂,即当装饰器必须 return 与给定函数 func
相同类型的可调用对象时,正如 MisterMiyagi 在评论中指出的那样。
import functools
from typing import *
C = TypeVar('C', bound=Callable)
def decorator(func: C) -> C:
func.attr1 = "spam"
func.attr2 = "eggs"
return func
现在我该怎么办?我无法从类型变量继承:
class CallableWithAttrs(C, Protocol):
attr1: str
attr2: str
error: Invalid base class "C"
自 typing.Callable
corresponds to collections.abc.Callable
以来,您只需定义一个 Protocol
实现 __call__
:
class CallableWithAttrs(Protocol):
attr1: str
attr2: str
def __call__(self, *args, **kwargs): pass
一个人可以参数化一个Protocol
一个Callable
:
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
的交集。
接受任何可调用函数 C
的函数因此可以 return CallableObj[C]
,具有相同签名且具有所需属性的可调用函数:
def decorator(func: C) -> CallableObj[C]: ...
MyPy 正确识别签名和属性:
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"?