我的代码嵌套太深了。有更好的方法吗?

2024-05-19 13:32:01 发布

您现在位置:Python中文网/ 问答频道 /正文

这段代码是我最近在另一个问题中写的,我不确定它是最优的。不过,我找不到一个减少缩进的方法来做这件事。有?在

def msg_generator(self):
    ''' Provides messages until bot dies '''
    while self.alive:
        for msg in self.irc.recv(self.buffer).split(('\r\n').encode()):
            if len(msg) > 3:
                try: 
                    yield Message(msg.decode())
                except Exception as e:
                    self.log('%s %s\n' % (except_str, str(e)))

我听不下去了,但我听太多了。现在有四个凹痕。在


Tags: 方法代码selfdefbotmsggeneratorprovides
2条回答

上面代码的问题并不是嵌套太深,而是代码的扩展性太强,不够有意。我的意思是,当你组装机器所需的齿轮、轮子和杠杆已经就位并(大概)起作用时,你能看到的有标签的模块太少了,有太多的问题被“完成”在太多隐含的、未标记的方式上,导致代码难以阅读(对于“完整”部分,你愿意听听吗http://www.infoq.com/presentations/Simple-Made-Easy-这是一个非常有启发性的谈话,它指出了代码的问题)。在

我举个例子:

...
for msg in self.irc.recv(self.buffer).split(('\r\n').encode()):
...

我想你说的是“信息”。那么为什么不调用这个变量message?为什么是缩写。那个词?message更清楚了。在

现在是for message in xxx。什么是xxx?你已经知道了。在python英语中,确实,for apple in appples; for pear in pears应该是一种好的、合理的语义书写方式:for ... in循环从元素集合中一次一个地挑出元素,因此{}在大多数情况下是可以的。这给了我们

^{pr2}$

这些信息来自哪里?根据您的代码,它们是执行self.irc.recv(self.buffer).split(('\r\n').encode())操作的结果。在

这真是个大麻烦。{{10}绝对是irc.recv()来解码所有的缓冲区返回,而不是先拆分,然后逐行解码。在

但这是次要的问题。重要的是:无论需要什么低级代码来提供irc中的下一行列表,它可能不应该出现在这里。更确切地说,像

messages = self.irc.get_messages()

或者也许

messages = <SOME_LIBRARY>.get_irc_messages( self.irc )

是合适的。当然,这些细节还有很多争议。就我个人而言,我尽量不把我的数据状态对象(主要是泛型类型)与我的库(库中的命名功能的无状态集合)合并在一起。上面链接的简单易用的演示文稿提出了完全相同的观点,顺便说一句,而且还设法令人信服地进行了辩论。(旁白:这当然与当前主流的OOP思想不谋而合,但是google搜索Yegge在名词王国中的执行,如果你相信OOP ≡ goodn'importe quois

目前的方法都是关于回复消息,而不是接收消息。把它想象成报纸上的一个真正的专栏:这里我们在办公室里,负责思考并汇编答案,然后发给读者;那些打开收到的报纸邮件、拿起电话、筛选电子邮件的人,在另一个办公室里有着完全不同的工作。因此,如何打开信封应该是你应该在这个地方实现的。需要外包。在

是否要将messages变量显式化或将其合并为内联API调用,取决于可用空间等多个因素,以及是否希望在例程的另一个点循环使用该返回值。我觉得这很好:

...
for message in self.irc.get_messages():
...

for ... in将透明地与生成器和列表返回器一起工作,这很好。在

总是用最一般的、但不松散的、最模块化的、但不是“原子化”的方式来思考代码。在

如果我要针对您的代码编写测试用例,我肯定会挑出irc.recv部分并单独测试。用这样的方式来思考你的代码:什么是好的模块,每个模块都有一个单独的关注点,一个合理的隔离级别,健全的调用参数,这些都有利于良好的可测试性?我的代码部分的合理名称是什么?-如果你坚持这样做,你的代码就不会再嵌套太深了。在

上面代码的问题并不是嵌套太深,而是代码的扩展性太强,不够有意。我的意思是,当你组装机器所需的齿轮、轮子和杠杆已经就位并(大概)起作用时,你能看到的有标签的模块太少了,有太多的问题被“完成”在太多隐含的、未标记的方式上,导致代码难以阅读(对于“完整”部分,你愿意听听吗http://www.infoq.com/presentations/Simple-Made-Easy-这是一个非常有启发性的谈话,它指出了代码的问题)。在

我举个例子:

...
for msg in self.irc.recv(self.buffer).split(('\r\n').encode()):
...

我想你说的是“信息”。那么为什么不调用这个变量message?为什么是缩写。那个词?message更清楚了。在

现在是for message in xxx。什么是xxx?你已经知道了。在python英语中,确实,for apple in appples; for pear in pears应该是一种好的、合理的语义书写方式:for ... in循环从元素集合中一次一个地挑出元素,因此{}在大多数情况下是可以的。这给了我们

^{pr2}$

这些信息来自哪里?根据您的代码,它们是执行self.irc.recv(self.buffer).split(('\r\n').encode())操作的结果。在

这真是个大麻烦。{{10}绝对是irc.recv()来解码所有的缓冲区返回,而不是先拆分,然后逐行解码。在

但这是次要的问题。重要的是:无论需要什么低级代码来提供irc中的下一行列表,它可能不应该出现在这里。更确切地说,像

messages = self.irc.get_messages()

或者也许

messages = <SOME_LIBRARY>.get_irc_messages( self.irc )

是合适的。当然,这些细节还有很多争议。就我个人而言,我尽量不把我的数据状态对象(主要是泛型类型)与我的库(库中的命名功能的无状态集合)合并在一起。上面链接的简单易用的演示文稿提出了完全相同的观点,顺便说一句,而且还设法令人信服地进行了辩论。(旁白:这当然与当前主流的OOP思想不谋而合,但是google搜索Yegge在名词王国中的执行,如果你相信OOP ≡ goodn'importe quois

目前的方法都是关于回复消息,而不是接收消息。把它想象成报纸上的一个真正的专栏:这里我们在办公室里,负责思考并汇编答案,然后发给读者;那些打开收到的报纸邮件、拿起电话、筛选电子邮件的人,在另一个办公室里有着完全不同的工作。因此,如何打开信封应该是你应该在这个地方实现的。需要外包。在

是否要将messages变量显式化或将其合并为内联API调用,取决于可用空间等多个因素,以及是否希望在例程的另一个点循环使用该返回值。我觉得这很好:

...
for message in self.irc.get_messages():
...

for ... in将透明地与生成器和列表返回器一起工作,这很好。在

总是用最一般的、但不松散的、最模块化的、但不是“原子化”的方式来思考代码。在

如果我要针对您的代码编写测试用例,我肯定会挑出irc.recv部分并单独测试。用这样的方式来思考你的代码:什么是好的模块,每个模块都有一个单独的关注点,一个合理的隔离级别,健全的调用参数,这些都有利于良好的可测试性?我的代码部分的合理名称是什么?-如果你坚持这样做,你的代码就不会再嵌套太深了。在

从我的头顶上,你可以这样做:

def msg_generator(self):
    ''' Provides messages until bot dies '''
    while self.alive:
        for msg in self.irc.recv(self.buffer).split(('\r\n').encode()):

            if len(msg) <= 3:
                continue

            try: 
                yield Message(msg.decode())
            except Exception as e:
                self.log('%s %s\n' % (except_str, str(e)))

或者你可以重构成函数。在

^{pr2}$

或者用这样的方法:

from itertools import imap

def msg_generator(self):
    ''' Provides messages until bot dies '''
    while self.alive:
        it = iter(self.irc.recv(self.buffer).split(('\r\n').encode()))
        return imap(handle_message, (msg for msg in it if len(msg) > 3)

def handle_message(msg):
    try: 
        return Message(msg.decode())
    except Exception as e:
        self.log('%s %s\n' % (except_str, str(e)))

最后一个选项并不完美,因为如果出现异常,那么函数将返回None,这不是一条真正的消息,因此您也可以在之后使用filter()或使用另一个端点句柄Nonemsg的

相关问题 更多 >