在Python中使用异常或返回代码更好吗?

时间:2009-07-20 09:18:45

标签: python performance exception

您可能知道Microsoft关于在.NET中使用异常的建议:

  

性能注意事项

     

...

     

仅抛出异常   非凡的条件,...

     

此外,不要抛出异常   当返回代码足够时......

(参见http://msdn.microsoft.com/en-us/library/system.exception.aspx的全文。)

作为比较点,你会推荐相同的Python代码吗?

6 个答案:

答案 0 :(得分:34)

pythonic要做的是提出和处理异常。优秀的书籍“简而言之的Python”在第6章的“错误检查策略”中对此进行了讨论。

这本书讨论了EAFP(“请求宽恕比允许更容易”)与LBYL(“在你跳跃之前看”)。

所以回答你的问题:

不,我不建议使用相同的python代码。我建议你阅读Python in a nutshell的第6章。

答案 1 :(得分:10)

理解例外的最佳方式是“if your method can't do what its name says it does, throw。”我个人认为,这个建议应该同样适用于.NET和Python。

关键的区别在于,您拥有的方法通常无法完成其名称所应执行的操作,例如,将字符串解析为整数或从数据库中检索记录。 C#样式是为了避免首先抛出异常:

int i;
if (Int32.TryParse(myString, out i)) {
    doWhatever(i);
}
else {
    doWhatever(0);
}

而Python对这种事情更加放心:

try:
    i = int(myString)
except ValueError:
    i = 0
doWhatever(i);

答案 2 :(得分:8)

在Python中,异常并不像其他语言那样昂贵,所以我不建议尝试避免异常。但是如果你确实抛出异常,你通常会想要在代码中的某个地方捕获它,例外情况是发生致命错误。

答案 3 :(得分:8)

通常,Python面向表达性 我会在这里应用相同的原则:通常,您希望函数返回结果(与其名称一致!)而不是错误代码。
因此,通常比返回错误代码更能引发异常。

但是,MSDN文章中所述的内容也适用于Python,它并不真正与返回错误代码而不是异常相关联。
在许多情况下,您可以看到用于正常流控制的异常处理,以及用于处理预期情况的异常处理。在某些环境中,这会对性能产生巨大影响;在所有环境中,它对程序表达能力和可维护性都有很大影响。

例外情况是异常情况,超出正常程序流程;如果你期望会发生某些事情,那么你应该直接处理,然后提出任何你不能期望/处理的东西。

当然,这不是一个配方,而只是一个启发式;最终的决定始终取决于开发人员和上下文,并且无法在一套固定的指南中说明 - 这对于异常处理来说更为真实。

答案 4 :(得分:4)

我做了一个简单的实验来比较使用以下代码提高异常的效果:

from functools import wraps
from time import time
import logging

def timed(foo):
    @wraps(foo)
    def bar(*a, **kw):
        s = time()
        foo(*a, **kw)
        e = time()
        print '%f sec' % (e - s)
    return bar

class SomeException(Exception):
    pass

def somefunc(_raise=False):
    if _raise:
        raise SomeException()
    else:
        return

@timed
def test1(_reps):
    for i in xrange(_reps):
        try:
            somefunc(True)
        except SomeException:
            pass

@timed
def test2(_reps):
    for i in xrange(_reps):
        somefunc(False)

def main():

    test1(1000000)
    test2(1000000)

    pass

if __name__ == '__main__':
    main()

得到以下结果:

  • 提出例外情况:3.142000秒
  • 使用return:0.383000秒

例外情况比使用return慢8倍。

答案 5 :(得分:3)

我认为是否返回错误代码或抛出异常是非常有效的思考,跨语言比较可能会有所帮助和提供信息。我想这个问题的非常普遍的答案就是考虑:任何函数的合法返回值集合应该尽可能小,并且必要时尽可能大

通常,这意味着如果给定方法在单个测试用例中返回整数,则用户可以正确地期望该方法始终返回整数抛出一个例外。但是,当然,概念上最简单的方式并不总是处理事物的最佳方式。

最少惊喜的回报通常是None;如果你研究它,你会发现它是None的语义,它全面地使用它:它是一个单例,不可变的值,在很多情况下,它的计算结果为{{1或者禁止进一步计算 - 没有连接,没有算术。因此,如果您选择编写一个False方法返回一个字符串输入的数字,并frob(x)表示非数字字符串和任何其他输入,并在None之类的表达式中使用它},你仍然得到一个非常接近虚假事件的例外。当然,如果将a=42+frob('foo')填入尚未使用frob('foo')定义的数据库列中,则可能在几个月后遇到问题。这可能是合理的,也可能是不合理的。

因此,在大多数情况下,例如想要从字符串中派生一个数字,使用像NOT NULLfloat(x)之类的somwething是可行的方法,因为这些内置函数会在没有给出可消化输入的情况下引发异常。如果这不适合您的用例,请考虑从自定义方法返回int(x);基本上,这个返回值告诉消费者“对不起,我无法理解你的输入。” 但是如果你肯定知道你的计划中的进展从那时起就有意义,你只想这样做。

你知道,我刚刚发现了如何将每个通知,警告和错误消息转换为可能显示停止的异常,例如,PHP。它只是让我疯狂,变量名中的拼写错误会在标准的PHP配置中生成,只是通知用户。这是如此糟糕。该程序只是继续使用一个根本没有意义的程序代码!我无法相信人们会发现这是一个特色。

同样地,人们应该这样看:如果在任何给定的时间点,可以以合理的成本断言一段代码的执行没有意义 - 因为缺少值,所以边界,或者是意外类型,或者当数据库连接等资源出现故障时 - 必须最大限度地减少调试难题,将执行和手动控制分解到代码中任何感觉有权处理事故的级别。

经验表明,避免早期操作并允许虚假值进入您的数据只会让您的代码更难调试。过度热衷的类型转换的许多例子也是如此:允许将整数添加到浮点数是合理的。允许只将数字添加到数字的字符串是一种虚假的做法,可能会产生奇怪的,未本地化的错误,这些错误可能会出现在正在处理该数据的任何给定行上。