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

Python: Move dictionary within a class - with class methods as values - to another file

我有一个处理 TCP 连接的 class,当收到具有给定“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, message queues, threads for sending/receiving etc...
        from x import _rcv_functions

这显然会导致循环依赖。

我一直在寻找这个问题的解决方案,有些人建议使用类型提示,但我无法在这种情况下使用它。

我也看到一些答案建议对每个字典值使用 __import__('module_name').ComManager.class_method 之类的东西,但我读到这会严重影响性能,因为每次我调用时都会处理整个文件 __import__,这远非理想,因为字典将包含数百个条目。

你试过了吗?

如果将 import 语句放在 __init__ 方法中,如上所示,将不会有“循环依赖”:在第一次导入另一个模块时,定义 ComManager 的调用者模块已经 运行,并且 class 已定义并准备在第二个模块中导入。

除此之外,您可以只将处理方法放在混合 class 中,而不是放在处理程序 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