我的代码太深了。有没有更好的办法?

时间:2013-10-14 15:17:38

标签: 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)))

我一直听说嵌套太多是坏事,但这似乎是必要的。它目前有四个缩进深度。

2 个答案:

答案 0 :(得分:10)

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

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)))

或者你可以重构为函数。

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 not len(msg) > 3:
                continue

            yield handle_message(msg)

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

或者使用这样的东西:

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)))

最后一个选项并不完美,因为如果有异常,那么func将返回None这不是真正的消息,所以您也可以使用filter()过滤后再使用另一个结束句柄None msg's。

答案 1 :(得分:4)

上面代码的问题并不是它嵌套太深,而是代码太扩展,而不是故意。我的意思是,虽然你需要组装你的机器的齿轮,轮子和杠杆到位并且(可能)起作用,但是看到的标签模块太少了 - 太多隐含的太多关注“被收集”了,未标记的方式,导致难以阅读的代码(对于'complect'部分,帮助您倾听http://www.infoq.com/presentations/Simple-Made-Easy - 这是一个非常有启发性的演讲,并且它指出了代码的问题)。

让我举个例子:

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

我认为你在这里谈论'消息'。那么为什么不调用那个变量message?为什么abbrv。那个词? message更加清晰。

现在是for message in xxx。什么是xxx?好吧,你已经知道了。在Pythonic英语中,大多数时候,for apple in appples; for pear in pears应该是一种良好的,合理的语义写作方式:for ... in循环单独输出元素,一次一个,一个元素的集合,所以for <singular> in <plural>大部分时间都可以。这给了我们

...
for message in messages:
...

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

现在这真是太糟糕了。对于初学者来说,('\r\n').encode()绝对与'\r\n'.encode()相同,这是一个较低的括号。现在这肯定是一个常数,所以为什么要在每个回合重新计算它?最好通过irc.recv()解码所有缓冲区返回,而不是先逐行拆分然后逐行解码。

但这是次要问题。重要的是:无论需要什么样的低级代码来提供irc的下一个行列表,它都可能不会出现在这个位置。而是像

这样的东西
messages = self.irc.get_messages()

或者

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

是合适的。当然,细节受到许多有争议的讨论。就个人而言,我试图不将我的大多数泛型类型的数据状态对象与没有添加到其中的功能混淆在一起 - 另一方面我的库 - 无状态的命名功能集合。上面链接的简单易用演示文稿使得完全相同的点,顺便说一句,也设法令人信服地进行争论。 (除此之外:当然,如果您相信OOP ≡ good n'importe quois EM>。)

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

是否要将messages变量显式或将其与内联API调用混淆取决于可用空间等数量因素以及是否要在例程中的其他位置回收该返回值。这对我来说很好看:

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

for ... in将透明地与发电机和列表返回者一起工作,这很好。

始终以最一般但尚未浮动,最模块化但未“雾化”的方式来思考您的代码。

如果我要针对您的代码编写测试用例,我肯定会单独列出irc.recv部分并单独测试它。以这种方式思考你的代码:什么是好的模块,每个都有一个关注点和一个合理的隔离级别,声音调用参数,这些都具有良好的可测试性?我的代码部分有什么合理的名称? - 如果你坚持这一点,你的代码就会停止嵌套太深,一直都是。