Python如果vs try-except

时间:2010-10-14 03:21:11

标签: python performance

我想知道为什么try-except比下面程序中的if慢。

def tryway():
    try:
        while True:
            alist.pop()
    except IndexError:
        pass

def ifway():
    while True:
        if alist == []: 
            break
        else:
            alist.pop()
if __name__=='__main__':
    from timeit import Timer
    alist = range(1000)
    print "Testing Try"
    tr = Timer("tryway()","from __main__ import tryway")
    print tr.timeit()
    print "Testing If"
    ir = Timer("ifway()","from __main__ import ifway")
    print ir.timeit()

我得到的结果很有趣。

Testing Try
2.91111302376
Testing If
0.30621099472

任何人都可以解释为什么尝试这么慢?

7 个答案:

答案 0 :(得分:41)

你只设置一次alist。第一次调用“tryway”会清除它,然后每次连续调用都不会执行任何操作。

def tryway():
    alist = range(1000)
    try:
        while True:
            alist.pop()
    except IndexError:
        pass

def ifway():
    alist = range(1000)
    while True:
        if alist == []:
            break
        else:
            alist.pop()
if __name__=='__main__':
    from timeit import Timer
    print "Testing Try"
    tr = Timer("tryway()","from __main__ import tryway")
    print tr.timeit(10000)
    print "Testing If"
    ir = Timer("ifway()","from __main__ import ifway")
    print ir.timeit(10000)

>>> Testing Try
>>> 2.09539294243
>>> Testing If
>>> 2.84440898895

答案 1 :(得分:4)

在大多数语言中,异常处理通常很慢。大多数编译器,解释器和VM(支持异常处理)将异常(语言习语)视为异常(不常见)。性能优化涉及权衡,快速制作异常通常意味着语言的其他区域会受到影响(无论是性能还是设计简单)。

在更技术层面,异常通常意味着VM /解释器(或运行时执行库)必须保存一堆状态并开始关闭函数调用堆栈上的所有状态(称为展开),直到找到有效捕获(除外)的点。

或者从不同的角度来看,程序在发生异常并且“调试器”接管时停止运行。此调试器在堆栈中搜索(调用函数数据)以查找与异常匹配的catch。如果它找到一个,它会清理一些东西并在那时将控制权返回给程序。如果找不到,那么它会将控制权返回给用户(可能是以交互式调试器或python REPL的形式)。

答案 2 :(得分:3)

如果你真的对速度感兴趣,那么你的两位参赛者都可以减轻体重。

while True:慢于while 1: - True是一个加载和测试的全局“变量”; 1是一个常量,编译器执行测试并发出无条件跳转。

在ifway中,

while True:是多余的。将while / if / break折叠起来:while alist != []:

while alist != []:编写while alist:

是一种缓慢的方式

试试这个:

def tryway2():
    alist = range(1000)
    try:
        while 1:
            alist.pop()
    except IndexError:
        pass

def ifway2():
    alist = range(1000)
    while alist:
        alist.pop()

`

答案 3 :(得分:2)

仍然有更快的迭代方式,虽然有时我们希望列表实际上是shirink所以我们知道剩下多少。然后alist应该是生成器的参数。 (John也适用于while alist:)我将函数作为生成器并使用了列表(ifway())等,因此这些值实际上是在函数外使用的(甚至没有使用过):

def tryway():
    alist = range(1000)
    try:
        while True:
            yield alist.pop()
    except IndexError:
        pass

def whileway():
    alist = range(1000)
    while alist:
         yield alist.pop()

def forway():
    alist = range(1000)
    for item in alist:
         yield item

if __name__=='__main__':
    from timeit import Timer
    print "Testing Try"
    tr = Timer("list(tryway())","from __main__ import tryway")
    print tr.timeit(10000)
    print "Testing while"
    ir = Timer("list(whileway())","from __main__ import whileway")
    print ir.timeit(10000)
    print "Testing for"
    ir = Timer("list(forway())","from __main__ import forway")
    print ir.timeit(10000)

J:\test>speedtest4.py
Testing Try
6.52174983133
Testing while
5.08004508953
Testing for
2.14167694497

答案 4 :(得分:0)

不确定,但我认为它是这样的:while true遵循正常的指令行,这意味着处理器可以管道并做各种好事。异常直接跳过所有这些,因此VM需要特别处理它,这需要时间。

答案 5 :(得分:0)

只是想把它扔到混合中: 我在下面尝试了以下脚本,这似乎表明处理异常比处理else语句要慢:

import time

n = 10000000
l = range(0, n)
t0 = time.time()
for i in l:
    try:
        i[0]
    except:
        pass
t1 = time.time()
for i in l:
    if type(i) == list():
        print(i)
    else:
        pass
t2 = time.time()
print(t1-t0)
print(t2-t1)

给出:

5.5908801555633545
3.512694835662842

所以,(尽管我知道有人可能会评论使用time而不是timeit),但使用try / except in循环似乎会减慢约60%。因此,在经历数十亿项的for循环时,最好与if/else一起使用。

答案 6 :(得分:-1)

防御性编程需要对罕见和/或异常的条件进行一次测试,其中一些在一年或多年内不会发生,因此在这些情况下可能尝试 - 除非可能是合理的。