子类化消息以添加其他行为

时间:2019-03-07 16:00:11

标签: python-3.x grpc-python

不确定为什么无法正常工作,我想对消息进行子类化并添加其他行为:

import data_pb2 as pb2

class Status(pb2.Status):
    def __init__(self, streamer, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.streamer = streamer

    def __setattr__(self, key, value):
        super().__setattr__(key, value)
        self.streamer.send_update()

当有人更改pb2.Status消息时,我希望调用send_update。 这是我收到的无用的错误消息:

Traceback (most recent call last):
  File "server.py", line 62, in <module>
    class Status(pb2.Status):
  File "C:\AppData\Local\conda\conda\envs\lib\site-packages\google\protobuf\internal\python_message.py", line 126, in __new__
    descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
KeyError: 'DESCRIPTOR'

2 个答案:

答案 0 :(得分:0)

只是发现了一个不幸的事实,即我们无意扩展消息类:

https://developers.google.com/protocol-buffers/docs/pythontutorial

协议缓冲区和O-O设计协议缓冲区类基本上是哑数据持有者(如C中的结构);他们没有在对象模型中成为优秀的一等公民。如果要向生成的类添加更丰富的行为,最好的方法是将生成的协议缓冲区类包装在特定于应用程序的类中。如果您无法控制.proto文件的设计,则包装协议缓冲区也是一个好主意(例如,如果您要重用另一个项目中的文件)。在这种情况下,您可以使用包装器类来设计一个更适合您的应用程序独特环境的接口:隐藏一些数据和方法,公开便利功能等。您绝不应通过继承将行为添加到生成的类中从他们那里。这将破坏内部机制,而且也不是一个好的面向对象的实践。

答案 1 :(得分:0)

我想出了一个可行的解决方案。消息更新时,我有一个线程事件,其set方法被调用。

class Status:
    def __init__(self, *args, **kwargs):
        self.status = pb2.Status(*args, **kwargs)
        self.event = None

    def __setattr__(self, key, value):
        if key == 'status' or key == 'event':
            super().__setattr__(key, value)
        else:
            super().__getattribute__('status').__setattr__(key, value)
            super().__getattribute__('event').set()

    def __getattr__(self, item):
        if item == 'event' or item == 'status':
            return super().__getattribute__(item)
        else:
            return super().__getattribute__('status').__getattribute__(item)


event = threading.Event()
status = Status(version="1",
                )
status_streamer = StatusStreamer(status, event)
status.event = event
status.version = str(int(status.version) + 1) #this triggers set to be called inside setattr, which results in the threads in SatusStreamer to stream the update

这有点古怪,但是因为我们不能将消息子类化,所以可以接受。状态是消息,事件是线程事件,当分配并启动这些项目时,它们不会触发设置的事件。但是,如果将其他任何属性分配给它,则会触发.set(),从而向客户端生成更新。