在twisted.words.protocols.irc.IRCClient中,似乎存在一些奇怪的冗余方法。例如,存在一个方法’privmsg’,还有一个方法’irc_PRIVMSG’。另一个例子是’join’和’irc_JOIN’。我想知道为什么会出现这种冗余,这仅仅是两个不同类型的用例吗?我们应该只使用一种而不使用另一种吗?
2、解决方案
两种不同类型的方法确实用于不同的上下文。这可以通过检查IRCClient处理接收到的数据的方式很容易地看到。首先,它将它们解析为行,然后将行拆分并将其传递给自己的handleCommand方法:
def handleCommand(self, command, prefix, params):
"""Determine the function to call for the given command and call
it with the given arguments.
"""
method = getattr(self, "irc_%s" % command, None)
try:
if method is not None:
method(prefix, params)
else:
self.irc_unknown(prefix, command, params)
except:
log.deferr()
这是Twisted协议实现中非常常见的模式的一个例子,而且在整个Python程序中也很常见。输入的某些部分用于动态构造方法名。然后使用getattr查找该方法。如果找到它,则调用它。
由于服务器向客户端发送诸如“PRIVMSG …”和“JOIN …”之类的行,因此导致IRCClient查找irc_PRIVMSG和irc_JOIN等方法。
这些irc_*方法仅通过拆分但未解析的行其余部分进行调用。这提供了消息附带的所有信息,但并不总以最友好的格式提供数据。例如,JOIN消息包含包含主机名的用户名,但通常主机名无关紧要,只需要昵称。因此,JOIN会执行irc_*方法中相当典型的事情:它将原始数据转换为更便于使用的内容,并将结果传递给userJoined:
def irc_JOIN(self, prefix, params):
"""
Called when a user joins a channel.
"""
nick = string.split(prefix,'!')[0]
channel = params[-1]
if nick == self.nickname:
self.joined(channel)
else:
self.userJoined(nick, channel)
您会看到这里还有条件,有时它调用joined而不是userJoined。这是从低级数据转换到更高层数据以方便应用程序开发人员使用数据的另一个示例。
这种分层结构可以帮助您决定在处理事件时覆盖哪些方法。如果最高级别的回调,例如userJoined、joined或privmsg对于您的需求足够,那么您应该使用它们,因为它们将使您的任务最轻松。另一方面,如果它们以不方便的格式提供数据或在其他方面使用起来很麻烦,则可以降至irc_*级别。您的方法将被调用,而不是在IRCClient上定义的方法,因此您可以以较低级别的格式处理数据,并且不会调用较高级别的回调(除非在覆盖方法时还调用基本实现)。
您还会发现有IRC消息,IRCClient甚至没有定义irc_*方法。如我们在上面的handleCommand方法中所看到的,这些都转到irc_unknown回调。但是,如果您在IRCClient子类上定义了一个irc_*方法,那么handleCommand将开始将数据传递给该方法。显然,在这些情况下,您唯一的选择是定义irc_*方法,因为没有更高级别的回调(如irc_PRIVMSG/privmsg案例中的privmsg)。
您可以像IRCClient那样组织irc_*方法的实现,如果您愿意的话 - 我通常发现这样做很有帮助,因为它使单元测试更容易,并将协议解析逻辑与应用程序逻辑分开 - 但由您决定。