有时我必须检查一些在循环内没有改变的条件,这意味着在每次迭代中都会对测试进行评估,但我认为这不是正确的方法。
我认为因为条件在循环内部没有变化,所以我应该只在循环外测试一次,但是我必须“重复自己”并且可能多次写入相同的循环。这是一个显示我的意思的代码:
#!/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()
在前面的代码上运行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>)
这表明,显然,在循环外进行一次测试可以提供更好的性能,但是我必须编写两次相同的循环(如果有elif
s,可能会更多。)
我知道在大多数情况下这种性能无关紧要,但我需要知道编写这种代码的最佳方法是什么。例如,有没有办法告诉python只评估测试一次?
感谢任何帮助,谢谢。
实际上,经过一些测试后,我现在确信性能上的差异主要受到循环内执行的其他代码的影响,而不是测试评估。所以现在我坚持使用第一种形式,这种形式更具可读性,并且更适合以后的调试。
答案 0 :(得分: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)
大部分性能差异都消失了。
通常,只要有公共代码,就应该尝试封装它。如果if
的分支除了循环之外没有任何共同点,那么尝试封装循环迭代器,例如:进入发电机。
答案 1 :(得分: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)
答案 2 :(得分:3)
python有像闭包,lambda函数,给函数的第一类状态和许多内置函数,这真的帮助我们删除重复的代码,例如想象你需要将函数应用于一系列值,你可以这样做吗
def outside():
if x: # x is a flag or it could the function itself, or ...
fun = sum # calc the sum, using pythons, sum function
else:
fun = lambda values: sum(values)/float(len(values)) # calc avg using our own function
result = fun(xrange(101))
如果您给我们一个确切的场景,我们可以帮助您优化它。
答案 3 :(得分:2)
我知道在这个方向上没有提供支持的解释语言,编译语言很可能只进行一次比较(循环不变优化),但如果x的评估很简单,那就无济于事。 显然,代替pass语句的代码不能完全相同,因为“if”就没有用了。通常,人们会编写在两个地方调用的过程。
答案 4 :(得分:1)
def outside():
def true_fn(a):
pass
def false_fn(a):
pass
fn = true_fn if x else false_fn
for a in xrange(n):
fn(a)
答案 5 :(得分:0)
在您的情况下,这取决于您的需求:可读性或性能。
如果您正在执行的任务是某种过滤器,您还可以使用list_comprehension
来运行循环:
[e for e in xrange(n) if x]
如果您展示一些代码,我可以提出建议。
答案 6 :(得分:0)
根据您的原始问题,您希望在不花费大量系统资源的情况下测试x的值,您已经接受了将全局x的值复制到局部变量的答案。
现在,如果返回x的值涉及多步函数,但是你保证结果对于x总是相同的,那么我会考虑记忆该函数。 这是一个关于主题的非常好的stackoverflow link