【问题标题】:subclassing a message to add additional behavior子类化消息以添加其他行为
【发布时间】:2019-07-29 14:08:35
【问题描述】:

不确定为什么这不起作用,我想对消息进行子类化并添加其他行为:

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'

【问题讨论】:

    标签: python-3.x grpc-python


    【解决方案1】:

    刚刚发现了一个不幸的事实,即我们不打算扩展消息类:

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

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

    【讨论】:

      【解决方案2】:

      我想出了一个可行的解决方案。当消息更新时,我有一个线程事件并且它的 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
      

      这有点 hacky,但因为我们不能对消息进行子类化,所以这是可以接受的。 status 是消息,event 是线程事件,当这些项目被分配并启动时,它们不会触发正在设置的事件。但是,当任何其他属性被分配给它时,它会触发 .set() ,从而向客户端生成更新。

      【讨论】:

        猜你喜欢
        • 2013-04-20
        • 2021-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-01
        相关资源
        最近更新 更多