如何告诉 mypy 类装饰器向装饰类添加方法

问题描述 投票:0回答:2

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'\x0A\x05hello\x10\x01\x18\x0A'

在我的应用程序代码中,我有一个特定的用例,我需要传递一个具有

dumps()
方法的对象。它可以是像上面这样的
pure_protobuf
Message
实例,也可以是任何其他类型,只要它实现
dumps()
即可。

对于我自己定义的类并实现

dumps()
“接口”,它工作得很好,但是对于
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

我别无选择吗?

python mypy
2个回答
8
投票

不幸的是,你在这里真的没有解决方案,因为你需要(无论它是外部的,它并不重要)

message
装饰器返回输入类的
Intersection
(或类型理论中的
Meet
)以及 4 种方法的协议 (
dump
dumps
load
loads
)。它尚未包含在 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
不起作用。


0
投票

mypy 有一个 插件系统,它可以让你告知它你的装饰器的效果。然而,该插件系统仍处于实验阶段,文档中没有足够的信息来编写一个插件系统。相反,文档建议与 mypy 开发人员联系。

© www.soinside.com 2019 - 2024. All rights reserved.