1.对于Python迭代工具的相对速度,你能从本章中得出什么结论?
答:一般来说,列表推导式(list comprehensions)通常是最快的; 只有当所有迭代工具都必须调用函数时,map 函数打败了列表推导式; for 循环往往比列表推导式慢; 在不变的因素下,生成器函数和表达式比推导式慢。 在PyPy下,其中一些发现有所不同; 例如,map 通常会转换为不同的相对性能,并且列表推导似乎总是最快的,可能是由于函数级别(function-level)的优化。
至少在今天测试的Python版本的情况下,在所使用的测试机器上,以及对于计时类型的代码,如果这三个变量中的任何一个不同,则结果可能会有所不同。 使用自行开发的 timer 或标准库 timeit 来测试您的例子以获得更加与你相关的结果。 还要记住,迭代只是程序的一个组成部分:更多的代码可以提供更完整的图像。
2.对于不同Python之间的相对速度,你能从本章中得出什么结论?
答:通常,PyPy 1.9(实现Python 2.7)通常比 CPython 2.7更快,而 CPython 2.7通常比 CPython 3.3更快。在大多数情况下计时,PyPy 比 CPython 快10倍,而 CPython 2.7通常比 CPython 3.3快一点。在使用整数数学的情况下,CPython 2.7可以比CPython 3.3快10倍,而 PyPy 可以比 3.3 快 100 倍。在其他情况下(例如,字符串操作和文件迭代器),PyPy 可能比 CPython慢 10 倍,因为 timeit 和内存管理差异可能会影响某些结果。 pystone 基准(pystone benchmark)确认了这些相对排名,尽管由于计时代码导致它报告的差异大小不同。至少在今天测试的Python版本上,在所使用的测试机器上,以及对于定时类型的代码来说,如果这三个变量中的任何一个不同,这些结果可能会有所不同。使用自行开发的 timer 或标准库 timeit 来测试您的情况以获得与你更相关的结果。在对Python实现进行计时尤其如此,在每个新版本中都可以任意优化。
一些额外的东西:
1.自制版计时器:timer.py
import time, sys
if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
timer = time.perf_counter # or process_time
else:
timer = time.clock if sys.platform[:3] == 'win' else time.time
def total(reps, func, *pargs, **kargs):
"""
Total time to run func() reps times.
Returns (total time, last result)
"""
repslist = list(range(reps)) # Hoist out, equalize 2.x, 3.x
start = timer() # Or perf_counter/other in 3.3+
for i in repslist:
ret = func(*pargs, **kargs)
elapsed = timer() - start
return (elapsed, ret)
def bestof(reps, func, *pargs, **kargs):
"""
Quickest func() among reps runs.
Returns (best time, last result)
"""
best = 2 ** 32 # 136 years seems large enough
for i in range(reps): # range usage not timed here
start = timer()
ret = func(*pargs, **kargs)
elapsed = timer() - start # Or call total() with reps=1
if elapsed < best: best = elapsed # Or add to list and take min()
return (best, ret)
def bestoftotal(reps1, reps2, func, *pargs, **kargs):
"""
Best of totals:
(best of reps1 runs of (total of reps2 runs of func))
"""
return bestof(reps1, total, reps2, func, *pargs, **kargs)
timerseqs.py
import sys, timer
reps = 10000
repslist = list(range(reps))
def forLoop():
res = []
for x in repslist:
res.append(abs(x))
return res
def listComp():
return [abs(x) for x in repslist]
def mapCall():
return list(map(abs, repslist))
def genExpr():
return list(abs(x) for x in repslist)
def genFunc():
def gen():
for x in repslist:
yield abs(x)
return list(gen())
print(sys.version)
for test in (forLoop, listComp, mapCall, genExpr, genFunc):
bestof, (total, result) = timer.bestoftotal(5, 1000, test)
print ('%-9s: %.5f => [%s...%s]' % (test.__name__, bestof, result[0], result[-1]))
测试结果:
3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
forLoop : 1.03159 => [0...9999]
listComp : 0.53327 => [0...9999]
mapCall : 0.30551 => [0...9999]
genExpr : 0.88393 => [0...9999]
genFunc : 0.84215 => [0...9999]
>>>
从上的比较可以大致推出,哪些迭代工具比较快,当然这取决的因素很多
2.Python 标准库 timeit
>>> import math
>>> import timeit
>>> min(timeit.repeat(stmt="import math\nmath.sqrt(10000)", number=1000, repeat=5))
0.00022440998054662487
>>> min(timeit.repeat(stmt="10000 * .5", number=1000, repeat=5))
1.18974212455214e-05
>>> min(timeit.repeat(stmt="pow(10000, .5)", number=1000, repeat=5))
0.00021989716515236069
从上面可以看出,math.sqrt和pow内置函数对于开平方根都是很快的。
函数陷阱(function gotchas):
1.
>>>X = 99 #错误的做法
>>>def selector():
print(X)
X = 88
>>>selector()
Traceback (most recent call last):
File "", line 1, in
selector()
File "", line 2, in selector
print(X)
UnboundLocalError: local variable 'X' referenced before assignment
>>>
>>>X = 99
>>>def selector():
global X
print(X)
X = 88
>>>selector()
>>> 99
2.
>>>def saver(X = []): #参数是可变对象的情况,可能情况不是你想要的
X.append(1)
print(X)
>>>saver([2])
[2, 1]
>>>saver()
[1]
>>>saver()
[1, 1]
>>>saver()
[1, 1, 1]
>>>def saver(X = None):
if X is None:
X = []
X.append(1)
print(X)
>>>saver([2])
[2, 1]
>>>saver()
[1]
>>>saver()
[1]
>>>saver()
[1]
注:转载《Learning Python 5th Edition》[奥莱理]
1. What conclusions can you draw from this chapter about the relative speed of
Python iteration tools?
2. What conclusions can you draw from this chapter about the relative speed of the Pythons timed?
In general, list comprehensions are usually the quickest of the bunch; map beats list comprehensions in Python only when all tools must call functions; for loops tend to be slower than comprehensions; and generator functions and expressions are slower than comprehensions by a constant factor. Under PyPy, some of these findings differ; map often turns in a different relative performance, for example, and list comprehensions seem always quickest, perhaps due to function-level optimizations. At least that’s the case today on the Python versions tested, on the test machine used, and for the type of code timed—these results may vary if any of these three variables differ. Use the homegrown timer or standard library timeit to test your use cases for more relevant results. Also keep in mind that iteration is just one component of a program’s time: more code gives a more complete picture.
In general, PyPy 1.9 (implementing Python 2.7) is typically faster than CPython 2.7, and CPython 2.7 is often faster than CPython 3.3. In most cases timed, PyPy is some 10X faster than CPython, and CPython 2.7 is often a small constant faster than CPython 3.3. In cases that use integer math, CPython 2.7 can be 10X faster than CPython 3.3, and PyPy can be 100X faster than 3.3. In other cases (e.g., string operations and file iterators), PyPy can be slower than CPython by 10X, though timeit and memory management differences may influence some results. The pystone benchmark confirms these relative rankings, though the sizes of the differences it reports differ due to the code timed. At least that’s the case today on the Python versions tested, on the test machine used, and for the type of code timed—these results may vary if any of these three variables differ. Use the homegrown timer or standard library timeit to test your use cases for more relevant results. This is especially true when timing Python implementations, which may be arbitrarily optimized in each new release.