如何告诉mypy类装饰器向装饰的类添加了一个方法



Python库pure_protobuf强制用户使用数据类,用另一个装饰器:来装饰它们

# to be clear: these two decorators are library code (external)
@message
@dataclass
class SearchRequest:
query: str = field(1, default='')
page_number: int32 = field(2, default=int32(0))
result_per_page: int32 = field(3, default=int32(0))

这个@message装饰器为SearchRequest实例分配一个名为dumps:的方法

SearchRequest(
query='hello',
page_number=int32(1),
result_per_page=int32(10),
).dumps() == b'x0Ax05hellox10x01x18x0A'

在我的应用程序代码中,我有一个特定的用例,需要传递一个具有dumps()方法的对象。它可以是像上面一样的pure_protobufMessage实例,也可以是任何其他类型,只要它实现了dumps()

它对于我自己定义并实现CCD_;"接口";,但对于pure_protobuf数据类,它一直抱怨它们没有属性dumps()

更具挑战性的是,我自己并没有定义这些pure_protobuf数据类,这些数据类将由我的库的客户端定义,所以我不能简单地做一些(愚蠢的(事情,比如:

@message
@dataclass
class SearchRequest:
query: str = field(1, default='')
page_number: int32 = field(2, default=int32(0))
result_per_page: int32 = field(3, default=int32(0))

def dumps(self):
self.dumps() # that is Message.dumps from the decorator

我是不是别无选择了?

不幸的是,这里真的没有解决方案,因为您需要(无论是外部的,都不是很重要(message装饰器用4个方法(dumpdumpsloadloads(返回输入类和协议的Intersection(或类型论中的Meet(。它还没有在python类型系统中,也没有实现为类型检查器扩展。请参阅mypy问题中关于Intersection的讨论。

最有趣的是,根据本教程,可以使用pytype,而不标记message。如果您可以选择使用另一个类型检查器,您可以声明自己的message版本:

from typing import IO, Protocol, TYPE_CHECKING
from pure_protobuf.dataclasses_ import message as _message
class MessageMixin(Protocol):
def dumps(self) -> bytes: ...
def dump(self, io: IO) -> None: ...
# Other definitions can go here
def message(cls):
if TYPE_CHECKING:  # tweak
return type(cls.__name__, (MessageMixin, cls), {})
else:  # actually run on runtime
return _message(cls)

然后,库的用户可以安全地使用这个message实现,因为事实上,它只是出于类型检查的目的包装了现有的方法,而不会影响运行时。因此,如果没有,它们只会出现mypy错误(如果没有使用类型检查器,则不会(,但运行时不会受到影响。

但是,它再次对mypy无效。

最新更新