<p>我看到,虽然已经发布了一个更快的解决方案,但实际上没有人回答这个问题。事实上,这是一个很难回答的问题!基本的解释是函数调用相对昂贵。为了使这个结论具有说服力,我将不得不深入研究Python的内部结构。准备好了!在</p>
<p>首先,我将使用<a href="http://docs.python.org/library/dis.html#module-dis">^{<cd3>}</a>从“优化”解决方案中反汇编(您的第三个版本)<code>ProjectEulerFive</code>和{<cd2>}。这里有很多,但是快速扫描是确认代码根本不调用函数所需的全部内容:</p>
<pre><code>>>> dis.dis(ProjectEulerFive)
2 0 LOAD_FAST 0 (m)
3 STORE_FAST 1 (a)
3 6 LOAD_CONST 1 (11)
9 STORE_FAST 2 (start)
4 12 LOAD_FAST 2 (start)
15 STORE_FAST 3 (b)
5 18 SETUP_LOOP 64 (to 85)
>> 21 LOAD_FAST 3 (b)
24 LOAD_FAST 0 (m)
27 COMPARE_OP 0 (<)
30 POP_JUMP_IF_FALSE 84
6 33 LOAD_FAST 1 (a)
36 LOAD_FAST 3 (b)
39 BINARY_MODULO
40 LOAD_CONST 2 (0)
43 COMPARE_OP 3 (!=)
46 POP_JUMP_IF_FALSE 71
7 49 LOAD_FAST 1 (a)
52 LOAD_FAST 0 (m)
55 INPLACE_ADD
56 STORE_FAST 1 (a)
8 59 LOAD_FAST 2 (start)
62 STORE_FAST 3 (b)
9 65 JUMP_ABSOLUTE 21
68 JUMP_ABSOLUTE 21
11 >> 71 LOAD_FAST 3 (b)
74 LOAD_CONST 3 (1)
77 INPLACE_ADD
78 STORE_FAST 3 (b)
81 JUMP_ABSOLUTE 21
>> 84 POP_BLOCK
12 >> 85 LOAD_FAST 1 (a)
88 RETURN_VALUE
</code></pre>
<p>现在让我们看看<code>find_solution</code>:</p>
^{pr2}$
<p>很快我们就明白了:(a)这段代码复杂得多,但(b)它还调用了三个不同的函数。第一个调用仅仅是对<code>xrange</code>的单个调用,但其他两个调用出现在最外层的for循环中。第一个调用是对<code>all</code>的调用;我怀疑第二个调用是正在调用的生成器表达式的<code>next</code>方法。但是函数是什么并不重要,重要的是它们在循环中被调用。在</p>
<p>现在,你可能会想“有什么大不了的?”在这里。这只是一个函数调用;这里或那里几纳秒——对吗?但事实上,这些纳秒加起来。由于最外层的循环总共经过<code>232792560 / 20 == 11639628</code>个循环,因此任何开销都会乘以超过1100万个</em>的<em>。在<code>ipython</code>中使用<code>%timeit</code>命令的快速计时显示,在我的机器上,一个函数调用(全部是它本身)大约要花费120纳秒:</p>
<pre><code>>>> def no_call():
... pass
...
>>> def calling():
... no_call()
...
>>> %timeit no_call()
10000000 loops, best of 3: 107 ns per loop
>>> %timeit calling()
1000000 loops, best of 3: 235 ns per loop
</code></pre>
<p>因此,对于while循环中出现的每个函数调用,这将花费<code>120 nanoseconds * 11000000 = 1.32 seconds</code>更多的时间。如果第二个函数调用是对生成器表达式的<code>next</code>方法的调用,那么该函数将被调用更多次,每次通过genex迭代一次——平均每个循环可能调用3-4次。在</p>
<p>现在来验证这个假设。如果函数调用是问题所在,那么消除函数调用就是解决方法。让我们看看。。。在</p>
<pre><code>def find_solution(step):
for num in xrange(step, 999999999, step):
for n in check_list:
if num % n != 0:
break
else:
return num
return None
</code></pre>
<p>这是<code>find_solution</code>的一个版本,它几乎与原始版本使用<code>for/else</code>语法所做的完全相同。唯一的函数调用是外部调用<code>xrange</code>,这应该不会引起任何问题。现在,当我计时原始版本时,花了11秒:</p>
<pre><code>found an answer: 232792560
took 11.2349967957 seconds
</code></pre>
<p>让我们看看这个新的改进版管理了什么:</p>
<pre><code>found an answer: 232792560
took 2.12648200989 seconds
</code></pre>
<p>这比您在我的机器上最快版本的<code>ProjectEulerFive</code>的性能快一点:</p>
<pre><code>232792560
took 2.40848493576 seconds
</code></pre>
<p>一切又有意义了。在</p>