测试在循环内不会改变的条件

Amr*_*Amr 6 python optimization loops

有时我必须检查一些在循环内没有变化的条件,这意味着在每次迭代中都会对测试进行评估,但我认为这不是正确的方法.

我认为,因为条件在循环内部没有变化,我应该只在循环外测试一次,但是我必须"重复自己"并且可能不止一次地写同一个循环.这是一个显示我的意思的代码:

#!/usr/bin/python

x = True      #this won't be modified  inside the loop
n = 10000000

def inside():
    for a in xrange(n):
        if x:    #test is evaluated n times
            pass
        else:
            pass

def outside():
    if x:        #test is evaluated only once
        for a in xrange(n):  
            pass
    else:
        for a in xrange(n):
            pass

if __name__ == '__main__':
    outside()
    inside()
Run Code Online (Sandbox Code Playgroud)

cProfile在前面的代码上运行给出了以下输出:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.542    0.542    0.542    0.542 testloop.py:5(inside)
        1    0.261    0.261    0.261    0.261 testloop.py:12(outside)
        1    0.000    0.000    0.803    0.803 testloop.py:3(<module>)
Run Code Online (Sandbox Code Playgroud)

这表明,显然,在循环外进行一次测试可以提供更好的性能,但是我必须编写两次相同的循环(如果有一些elifs 则可能更多).

我知道,这样的表现也不会在大多数情况下无所谓,但我需要知道什么是写这种代码的最好方式.例如,有没有办法告诉python只评估一次测试?

任何帮助表示赞赏,谢谢.

编辑:

实际上,经过一些测试后,我现在确信性能上的差异主要受到循环内执行的其他代码的影响,而不是测试的评估.所以现在我坚持使用第一种形式,它更具可读性,更适合以后的调试.

eca*_*mur 5

首先,示例之间性能差异的一个主要组成部分是查找全局的时间.如果我们将其捕获到局部变量中:

def inside_local():
    local_x = x
    for a in xrange(n):
        if local_x:
            pass
        else:
            pass

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.258    0.258    0.258    0.258 testloop.py:13(outside)
    1    0.314    0.314    0.314    0.314 testloop.py:21(inside_local)
    1    0.421    0.421    0.421    0.421 testloop.py:6(inside)
Run Code Online (Sandbox Code Playgroud)

大部分性能差异消失了.

通常,只要有公共代码,就应该尝试封装它.如果if除了循环之外没有任何共同点,则尝试将循环迭代器封装到例如发生器中.


wro*_*ite 5

这是我在这种情况下通常做的事情.

def inside():
    def x_true(a):
        pass

    def x_false(a):
        pass

    if x:
        fn = x_true
    else:
        fn = x_false

    for a in xrange(n):
        fn(a)
Run Code Online (Sandbox Code Playgroud)