关于性能:Python中的廉价异常处理?

Cheap exception handling in Python?

我在前面的回答中读到,异常处理在Python中很便宜,所以我们不应该进行条件前检查。

我以前没听说过,但我对Python比较陌生。异常处理是指动态调用和静态返回,而if语句是静态调用和静态返回。

如何做的检查是坏的和try-except是好的,似乎是相反的方式。有人能给我解释一下吗?


不要把这些小东西都出汗。您已经选择了一种速度较慢的脚本语言,因此尝试优化到操作码对您没有多大帮助。选择像python这样的解释性动态语言的原因是为了优化您的时间,而不是CPU的时间。

如果您使用通用语言习惯,那么您将看到快速原型和干净设计的所有好处,并且随着新版本的python的发布和计算机硬件的升级,您的代码自然会运行得更快。

如果您有性能问题,那么分析您的代码并优化您的慢算法。但同时,在特殊情况下使用异常,因为它将使您最终按照这些行进行的任何重构变得容易得多。


您可能会发现这篇文章很有用:Try/Except Performance in python:一个简单的测试,其中patrick altman做了一些简单的测试,以查看在各种场景中的性能,预条件检查(在本例中,特定于字典键)和仅使用异常。如果您想使代码适应测试其他条件,也可以提供代码。

他得出的结论是:

From these results, I think it is fair
to quickly determine a number of
conclusions:

  • If there is a high likelihood that the element doesn't exist, then
    you are better off checking for it
    with has_key.
  • If you are not going to do anything with the Exception if it is
    raised, then you are better off not
    putting one have the except
  • If it is likely that the element does exist, then there is a very
    slight advantage to using a try/except
    block instead of using has_key,
    however, the advantage is very slight.

  • 撇开其他人所说的绩效衡量标准不谈,指导原则的结构通常是"请求原谅比请求许可更容易"和"三思而后行"。

    考虑这两个片段:

    1
    2
    3
    4
    # Look before you leap
    if not os.path.exists(filename):
        raise SomeError("Cannot open configuration file")
    f = open(filename)

    VS

    1
    2
    3
    4
    5
    # Ask forgiveness ...
    try:
      f = open(filename)
    except IOError:
      raise SomeError("Cannot open configuration file")

    当量?不是真的。操作系统是多接收系统。如果在"exists"和"open"调用的测试之间删除了文件,会发生什么情况?

    如果文件存在但不可读,会发生什么?如果是目录名而不是文件呢?可能有许多故障模式,检查所有这些模式需要大量工作。尤其是"开放"呼叫已经检查并报告了所有可能的故障。

    指导方针应该是减少状态不一致的可能性,最好的方法是使用异常而不是测试/调用。


    "有人能给我解释一下吗?"

    视情况而定。

    这里有一个解释,但没有帮助。你的问题源于你的假设。由于现实世界与你的假设相冲突,这一定意味着你的假设是错误的。没什么解释,但这就是你问的原因。

    异常处理是指动态调用和静态返回,而if语句是静态调用和静态返回。

    "动态呼叫"是什么意思?在堆栈帧中搜索处理程序?我想这就是你所说的。一个"静态调用"以某种方式将块定位在if语句之后。

    也许这个"动态调用"不是操作中最昂贵的部分。也许if语句表达式评估比简单的"尝试一下失败"稍微贵一些。

    结果发现,python的内部完整性检查几乎与if语句相同,无论如何都必须执行。因为python总是要检查,所以if语句(大部分)是多余的。

    您可以在http://docs.python.org/c-api/intro.html exceptions中了解低级异常处理。

    编辑

    更重要的一点是:除了辩论之外,如果辩论与否无关紧要。

    因为异常很便宜,所以不要将它们标记为性能问题。

    使用使代码清晰和有意义的代码。不要在这样的微优化上浪费时间。


    使用python,很容易检查速度的不同可能性-了解timeit模块:

    ... example session (using the command line) that compare the cost of using hasattr() vs. try/except to test for missing and present object attributes.

    1
    2
    3
    4
    5
    6
    7
    8
    % timeit.py 'try:' '  str.__nonzero__' 'except AttributeError:' '  pass'
    100000 loops, best of 3: 15.7 usec per loop
    % timeit.py 'if hasattr(str,"__nonzero__"): pass'
    100000 loops, best of 3: 4.26 usec per loop
    % timeit.py 'try:' '  int.__nonzero__' 'except AttributeError:' '  pass'
    1000000 loops, best of 3: 1.43 usec per loop
    % timeit.py 'if hasattr(int,"__nonzero__"): pass'
    100000 loops, best of 3: 2.23 usec per loop

    这些计时结果在hasattr()情况下显示,引发异常的速度很慢,但执行测试的速度比不引发异常的速度慢。因此,在运行时间方面,使用异常处理异常情况是有意义的。

    编辑:命令行选项-n将默认为足够大的计数,以便运行时间有意义。手册引述:

    If -n is not given, a suitable number of loops is calculated by trying successive powers of 10 until the total time is at least 0.2 seconds.


    我也是一个Python初学者。虽然我不能说明为什么在这个答案的上下文中异常处理被称为廉价的,但我的想法是:

    请注意,每次检查elif else是否必须评估一个条件。异常处理(包括对异常处理程序的搜索)仅在异常情况下发生,这在大多数情况下可能很少见。这是一个明显的效率提升。正如Jay所指出的,当密钥很可能不存在时,最好使用条件逻辑而不是异常。这是因为,如果密钥大部分时间都不存在,则它不是异常情况。

    也就是说,我建议你不要担心效率,更不要担心意义。当您想决定某件事情时,使用异常处理来检测异常情况和检查条件。昨天,S·洛特提醒我意义的重要性。

    例证:

    1
    2
    3
    4
    5
    def xyz(key):
       dictOb = {x:1, y:2, z:3}
       #Condition evaluated every time
       if dictOb.has_key(key):  #Access 1 to dict
            print dictOb[key]  #Access 2

    对战

    1
    2
    3
    4
    5
    6
    7
    #Exception mechanism is in play only when the key isn't found.
    def xyz(key):
       dictOb = {x:1, y:2, z:3}
       try:
            print dictOb[key]  #Access 1
       except KeyError:
            print"Not Found"

    总的来说,有一些代码可以处理某些事情,比如丢失的密钥,以防万一,需要处理异常,但是在大多数情况下,比如当密钥不存在时,您真正想要做的是决定是否存在该密钥=>是否存在。python强调并鼓励说你的意思。

    如果elif->

  • 当您在代码中看到异常情况(即异常/意外情况)时,它更清楚地表达了这一含义。
  • 它更干净,可读性更高。
  • 它更灵活。
  • 它可以用来编写更简洁的代码。
  • 避免了很多令人讨厌的检查。
  • 它更易于维护。
  • 注释当我们避免使用Try-Except时,异常会继续出现。未处理的异常只转到默认处理程序。使用Try-Except时,可以自己处理错误。它可能更有效,因为如果其他方法需要条件评估,而寻找异常处理程序可能更便宜。即使这是真的,从中获得的收益也太小了,不值得去想。

    我希望我的回答有帮助。


    正如S.Lott所说,一般的信息是"尝试/例外"不会造成伤害,因此您可以在适当的时候随意使用它。

    这场辩论通常被称为"lbyl与eafp"——即"先看后跳"与"请求宽恕比请求允许更容易"。亚历克斯·马泰利在这里谈到了这个问题:http://mail.python.org/pipermail/python-list/2003-may/205182.html这场辩论已经进行了近6年,但我认为基本问题没有太大的改变。


    静态调用和动态调用和返回是什么?为什么您认为调用和返回在python中有任何不同,这取决于您是否在try/except块中执行?即使没有捕捉到异常,python仍然必须处理可能引发某些事件的调用,因此在处理调用和返回的方式方面对python没有任何影响。

    python中的每个函数调用都涉及将参数推送到堆栈上,并调用可调用的。在python的内部连接中,每个函数终止之后都有调用方,检查是否成功或异常终止,并相应地处理它。换句话说,如果您认为在Try/Except块中有一些额外的处理,而当您不在一个块中时却被忽略了,那么您就错了。我假设这就是你们"静态"和"动态"区别的意义所在。

    此外,这是一个风格问题,经验丰富的Python开发人员会很好地捕获异常,因此当他们看到适当的Try/Except(尝试/排除)调用时,它比条件检查更具可读性。