Python:将类中的字典以类方法作为值移动到另一个文件

问题描述

我有一个处理TCP连接的类,当接收到带有给定“ ID”的消息时,我需要调用一个特定的函数来处理它。这些ID只是数字,所以我创建了一个IntEnum来保存ID:

class ID(IntEnum):
    # ...
    message_x = 20
    message_y = 21
    # ...

这些ID不必是连续的(即某些ID是保留的),我希望最终会有数百甚至数千个ID。

因为我不想为每个ID都创建一千个if-else,所以我在考虑将ID作为字典中的键使用,该字典包含对处理每个消息的功能的引用:

class ComManager:

    def __init__(self):
        # Init socket,message queues,threads for sending/receiving etc...
        self.rcv_functions = {#...
                              ID.message_x: ComManager._rcv_message_x,ID.message_y: ComManager._rcv_message_y,#...
                              }
        # Launch _rcv_thread here

    def _rcv_thread(self):
        message_id = self.rcv_message_id() # receive message ID from socket
        message_id = ID(message_id) # Change from type "int" to type "ID"
        self._rcv_functions[message_id](self) # Call appropriate method according to the dictionary,thus avoiding a massive if/else here or "switch case"-like workarounds

    def _rcv_message_x(self):
        # Handle reception and processing of message x here

    def _rcv_message_y(self):
        # Handle reception and processing of message y here

我一直试图将“ _rcv_functions”放入其自己的文件中,因为它已经很烦人了,每条消息都具有一个功能

# import ID and ComManager classes from their respetive files

_rcv_functions = {
    # ...
    ID.message_x:  ComManager._rcv_message_x,ID.message_y:  ComManager._rcv_message_y,# ...
}

然后,在ComManager中:

class ComManager:
    def __init__(self):
        # Init socket,threads for sending/receiving etc...
        from x import _rcv_functions

这显然导致循环依赖。

我一直在寻找解决此问题的方法,有人建议使用类型提示,但在这种情况下我无法使其正常工作。

我还看到了一些答案,建议为每个字典值使用类似__import__('module_name').ComManager.class_method的东西,但是我读到这会严重影响性能,因为每次我调用{{1 }},因为字典将包含数百个条目,因此远非理想。

解决方法

您甚至尝试过吗?

如果如上所示将import语句放置在__init__方法内,将没有“循环依赖”:在第一次导入另一个模块时,调用者定义了ComManager的模块已经运行,并且该类已定义并且可以导入到第二个模块中。

除此之外,您可以将处理方法放在mixin类中,而不是放在处理程序ComManager本身的正文中。

因此,在另一个模块中,您将拥有:


...
class ID(IntEnum):
    ...

class HandlersMixin:
    def _rcv_message_x(self,msg):
        ...
    ...

mapping = {
  ID.message_x = HandlerMixn._rcv_message_x,}

请注意,通过这种方式,映射将映射未绑定的方法:它们是纯函数,期望将“ HandlerMixin”的实例作为其第一个参数

在第一个模块上:

from other_module import ID,mapping,HandlerMixin

class ComManager(HandlerMixin):
    def _rcv_thread(self):
        message_id = self.rcv_message_id() # receive message ID from socket
        message_id = ID(message_id) # Change from type "int" to type "ID"
        mapping[message_id](self)  
        # Passing "self" explictly will make the methods work the same
        # as f they were called from this instance as `self.method_name`,# and since they are methods on this class through inheritance
        # they can use any other methods or attributes on this instance