Python语法背后的基本原理

时间:2010-04-10 01:16:17

标签: python performance

在Python中主张使用for i in xrange(...)样式循环结构的原因是什么?对于简单的整数循环,开销的差异很大。我使用两段代码进行了一个简单的测试:

档案idiomatic.py

#!/usr/bin/env python

M = 10000
N = 10000

if __name__ == "__main__":
    x, y = 0, 0
    for x in xrange(N):
        for y in xrange(M):
            pass

档案cstyle.py

#!/usr/bin/env python

M = 10000
N = 10000

if __name__ == "__main__":
    x, y = 0, 0
    while x < N:
        while y < M:
            y += 1
        x += 1

剖析结果如下:

bash-3.1$ time python cstyle.py

real    0m0.109s
user    0m0.015s
sys     0m0.000s

bash-3.1$ time python idiomatic.py

real    0m4.492s
user    0m0.000s
sys     0m0.031s

我可以理解为什么Pythonic版本更慢 - 我想它与调用xrange N次有很大关系,如果有一种方法来回放生成器,也许这可以被消除。但是,由于执行时间的这种差异,为什么人们更喜欢使用Pythonic版本?

编辑:我使用Martelli先生提供的代码再次进行了测试,现在结果确实更好了:

我以为我会在这里列举一下这个帖子的结论:

1)模块范围内的大量代码是个坏主意,即使代码包含在if __name__ == "__main__":块中。

2)*奇怪的是,将属于thebadone的代码修改为我的错误版本(让y增长而不重置)在性能上几乎没有差异,即使对于较大的M和N值也是如此。

5 个答案:

答案 0 :(得分:22)

这是正确的比较,例如在loop.py中:

M = 10000
N = 10000

def thegoodone():
   for x in xrange(N):
       for y in xrange(M):
           pass

def thebadone():
    x = 0
    while x < N:
        y = 0
        while y < M:
            y += 1
        x += 1

所有实质性代码都应该始终在函数中 - 在模块的顶层放置一亿个循环显示鲁莽地忽视性能并嘲弄任何测量表示表现。

完成后,您会看到:

$ python -mtimeit -s'import loop' 'loop.thegoodone()'
10 loops, best of 3: 3.45 sec per loop
$ python -mtimeit -s'import loop' 'loop.thebadone()'
10 loops, best of 3: 10.6 sec per loop

所以,正确衡量,你提倡的坏方法比Python推广的好方式慢大约3倍。我希望这会让你重新考虑你的错误宣传。

答案 1 :(得分:11)

您忘了在内循环后将y重置为0.

#!/usr/bin/env python
M = 10000
N = 10000

if __name__ == "__main__":
    x, y = 0, 0
    while x < N:
        while y < M:
            y += 1
        x += 1
        y = 0

ed:修正后的20.63s与使用xrange的6.97s

答案 2 :(得分:3)

适合迭代数据结构

for i in ...语法非常适合迭代数据结构。在较低级别的语言中,您通常会迭代一个由int索引的数组,但是使用python语法可以消除索引步骤。

答案 3 :(得分:1)

这不是这个问题的直接答案,但我想在xrange()上更多地打开对话框。两件事:

一个。没有人纠正的OP语句之一有问题(是的,除了重置y的代码中的错误):

“我想这与调用xrange N次有很大关系....”

与传统的计数 for 循环不同,Python更像是一个shell的 foreach ...循环遍历一个可迭代的循环。因此,xrange()恰好称为一次,而不是“N次”。

B中。 xrange()是Python 2中此函数的名称。它替换并在Python 3中重命名为range(),因此在移植时请记住这一点。如果你还不知道,xrange()返回一个迭代器(类似对象),而range()返回列表。由于后者的效率较低,因此不推荐使用xrange(),这对内存更友好。 Python 3中的解决方法,适用于需要列表的所有人list(range(N))

答案 4 :(得分:0)

我从@Alex Martelli's answer重复了测试。惯用for循环比while循环快 5 倍:

python -mtimeit -s'from while_vs_for import while_loop as loop' 'loop(10000)'
10 loops, best of 3: 9.6 sec per loop
python -mtimeit -s'from while_vs_for import for_loop as loop'   'loop(10000)'
10 loops, best of 3: 1.83 sec per loop

<强> while_vs_for.py

def while_loop(N):
    x = 0
    while x < N:
        y = 0
        while y < N:
            pass
            y += 1
        x += 1

def for_loop(N):
    for x in xrange(N):
        for y in xrange(N):
            pass

在模块级别:

$ time -p python for.py
real 4.38
user 4.37
sys 0.01
$ time -p python while.py
real 14.28
user 14.28
sys 0.01