子类化消息以添加额外的行为
subclassing a message to add additional behavior
不确定为什么这不起作用,我想对消息进行子类化并添加其他行为:
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'
刚刚发现一个不幸的事实,即我们并不打算扩展消息 classes:
https://developers.google.com/protocol-buffers/docs/pythontutorial
协议缓冲区和 O-O 设计协议缓冲区 classes 基本上是哑数据持有者(就像 C 中的结构);他们不会先成为对象模型中的 class 公民。如果您想向生成的 class 添加更丰富的行为,最好的方法是将生成的协议缓冲区 class 包装在特定于应用程序的 class 中。如果您无法控制 .proto 文件的设计(比如说,您正在重用另一个项目中的文件),那么包装协议缓冲区也是一个好主意。在这种情况下,您可以使用包装器 class 设计一个更适合您应用程序独特环境的接口:隐藏一些数据和方法,公开便利功能等。您永远不应该添加行为通过从 继承生成的 classes。这会破坏内部机制,无论如何都不是好的面向对象实践。
我想出了一个有效的解决方案。当消息更新时,我有一个线程事件,它的 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() 产生对客户端的更新。
不确定为什么这不起作用,我想对消息进行子类化并添加其他行为:
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'
刚刚发现一个不幸的事实,即我们并不打算扩展消息 classes:
https://developers.google.com/protocol-buffers/docs/pythontutorial
协议缓冲区和 O-O 设计协议缓冲区 classes 基本上是哑数据持有者(就像 C 中的结构);他们不会先成为对象模型中的 class 公民。如果您想向生成的 class 添加更丰富的行为,最好的方法是将生成的协议缓冲区 class 包装在特定于应用程序的 class 中。如果您无法控制 .proto 文件的设计(比如说,您正在重用另一个项目中的文件),那么包装协议缓冲区也是一个好主意。在这种情况下,您可以使用包装器 class 设计一个更适合您应用程序独特环境的接口:隐藏一些数据和方法,公开便利功能等。您永远不应该添加行为通过从 继承生成的 classes。这会破坏内部机制,无论如何都不是好的面向对象实践。
我想出了一个有效的解决方案。当消息更新时,我有一个线程事件,它的 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() 产生对客户端的更新。