Python:字典条目在类中消失

  • 本文关键字:消失 字典 Python python
  • 更新时间 :
  • 英文 :


我在课堂上遇到了一个关于字典的挑战。我确信我在监督一些事情,但我不确定具体是什么。情况如下:

我有一个叫做NetMessageHandler的类,它有函数onmessage()rcvtcpmsg()。函数onmessage()允许其他类钩入使用回调接收的特定网络消息,rcvtcpmsg()由TCP客户端调用,以处理接收到的原始消息为JSON并将其传递给系统。最后,_run_callbacks()在收到消息时被调用。

当我调用onmessage()时,回调存储在类内的字典中。当我在添加一些内容后打印()字典时,结果与预期一致,例如:

{'systemdescription': [<function system_description_handler at 0x7f70a57ee0>]}
Length: 1

然而,当我实际想使用存储的回调函数进行回调时,字典突然为空,函数失败。我不知道这是如何发生的,因为我没有清除/设置字典为新值。似乎在onmessage()函数完成后,字典被清空。

{}
Length: 0
到目前为止,该类的代码如下(仅是相关部分):
class NetMessageHandler():
def __init__(self):
# Create an empty dictionary to store 
self._callbacks = {}
def _run_callbacks(self, type: str, data: dict[str, Any], origin: NetMessageOrigin):
'''Runs when a message is received'''
print(f'{self.__class__.__name__}: Running callbacks for {type}')
print(self._callbacks)  # Added as a test, it returns an empty dictionary: '{}'

# This part never runs as the dictionary is empty
if type in self._callbacks:
for c in self._callbacks[type]:
c(type, data, origin)

def rcvtcpmsg(self, msg: str, origin: 'TCPClient') -> None:
'''Receive a TCP message and parse it to valid JSON, then run callbacks.'''
data = self._parseMessage(msg)
# Create an origin that represents the message sender
origin = NetMessageOrigin(NetMessageOriginType.TCP, origin)
# Run callbacks for the specific message type
if "t" in data:
self._run_callbacks(data["t"], data, origin)
def onmessage(self, type:str, callback:Callable[[str, dict[str, Any], NetMessageOrigin], Any]):
'''Adds a new callback for a specific message type.'''
# Check if a callback for this message already exists
if type not in self._callbacks:
print(f'{self.__class__.__name__}: New callback array created for "{type}"')
self._callbacks[type] = []
if callback not in self._callbacks[type]:
self._callbacks[type].append(callback)
print(f'{self.__class__.__name__}: Callback added for message type "{type}"')
else:
print(f'{self.__class__.__name__}: Callback already existed for message type "{type}"')

# The prints below output the expected value: {'systemdescription': [<function system_description_handler at 0x7f70a57ee0>]}
print(self._callbacks)
print("Length:", len(self._callbacks))

我检查了所有内容的顺序,并且在第一个消息到达之前创建了回调,这里可能发生了什么?

如果所提供的代码没有引起明显的原因,则很可能是调用/使用该类的代码存在问题。最可能的原因是调用代码实例化了这个类的多个实例,每个实例都有自己的self._callbacks字典,所以添加到一个实例不会影响另一个实例。考虑到该类的编写方式,避免这种情况的最简单方法(尽管我不推荐,除非用于调试目的)是删除def __init__。通过将_callbacks设置为类属性(而不是实例属性),所有的instancemethod都将简单地更新一个字典(类的),而不是它们自己的字典:

class NetMessageHandler():
_callbacks = {}
def _run_callbacks(..
...

似乎没有真正的理由让这个类有多个实例,一个更好的方法是将其方法更改为类方法。不仅当方法打印时它们引用类self.__class__.__name__,而且通过将方法绑定到类,无论回调是注册到NetMessageHandler().onmessage()还是NetMessageHandler.onmessage()都无关紧要。修改如下:

class NetMessageHandler():
_callbacks = {}
@classmethod
def _run_callbacks(cls, type: str, data: dict[str, Any], origin: NetMessageOrigin):
'''Runs when a message is received'''
print(f'{cls.__name__}: Running callbacks for {type}')
print(cls._callbacks)  # Added as a test, it returns an empty dictionary: '{}'
# This part never runs as the dictionary is empty
if type in cls._callbacks:
for c in cls._callbacks[type]:
c(type, data, origin)
@classmethod
def rcvtcpmsg(cls, msg: str, origin: 'TCPClient') -> None:
'''Receive a TCP message and parse it to valid JSON, then run callbacks.'''
data = cls._parseMessage(msg)
# Create an origin that represents the message sender
origin = NetMessageOrigin(NetMessageOriginType.TCP, origin)
# Run callbacks for the specific message type
if "t" in data:
cls._run_callbacks(data["t"], data, origin)
@classmethod
def onmessage(cls, type:str, callback:Callable[[str, dict[str, Any], NetMessageOrigin], Any]):
'''Adds a new callback for a specific message type.'''
# Check if a callback for this message already exists
if type not in cls._callbacks:
print(f'{cls.__name__}: New callback array created for "{type}"')
cls._callbacks[type] = []
if callback not in cls._callbacks[type]:
cls._callbacks[type].append(callback)
print(f'{cls.__name__}: Callback added for message type "{type}"')
else:
print(f'{cls.__name__}: Callback already existed for message type "{type}"')
# The prints below output the expected value: {'systemdescription': [<function system_description_handler at 0x7f70a57ee0>]}
print(cls._callbacks)
print("Length:", len(cls._callbacks))

这不是一个真正的单例,但在进入不太常见的__new__方法甚至元类之前,它可能是python所能达到的最接近的单例。

最新更新