预计阅读本页时间:-
Timing Results
When the script of the prior section is run under Python 3.0, I get these results on my Windows Vista laptop—map is slightly faster than list comprehensions, both are quicker than for loops, and generator expressions and functions place in the middle:
C:\misc> c:\python30\python timeseqs.py
3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)]
---------------------------------
forLoop : 2.64441 => [0...9999]
---------------------------------
listComp : 1.60110 => [0...9999]
---------------------------------
mapCall : 1.41977 => [0...9999]
---------------------------------
genExpr : 2.21758 => [0...9999]
---------------------------------
genFunc : 2.18696 => [0...9999]
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
If you study this code and its output long enough, you’ll notice that generator expressions run slower than list comprehensions. Although wrapping a generator expression in a list call makes it functionally equivalent to a square-bracketed list comprehension, the internal implementations of the two expressions appear to differ (though we’re also effectively timing the list call for the generator test):
return [abs(x) for x in range(size)] # 1.6 seconds
return list(abs(x) for x in range(size)) # 2.2 seconds: differs internally
Interestingly, when I ran this on Windows XP with Python 2.5 for the prior edition of this book, the results were relatively similar—list comprehensions were nearly twice as fast as equivalent for loop statements, and map was slightly quicker than list comprehensions when mapping a built-in function such as abs (absolute value). I didn’t test generator functions then, and the output format wasn’t quite as grandiose:
2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit (Intel)]
forStatement => 6.10899996758
listComprehension => 3.51499986649
mapFunction => 2.73399996758
generatorExpression => 4.11600017548
The fact that the actual 2.5 test times listed here are over two times as slow as the output I showed earlier is likely due to my using a quicker laptop for the more recent test, not due to improvements in Python 3.0. In fact, all the 2.6 results for this script are slightly quicker than 3.0 on this same machine if the list call is removed from the map test to avoid creating the results list twice (try this on your own to verify).
Watch what happens, though, if we change this script to perform a real operation on each iteration, such as addition, instead of calling a trivial built-in function like abs (the omitted parts of the following are the same as before):
# File timeseqs.py
...
...
def forLoop():
res = []
for x in repslist:
res.append(x + 10)
return res
def listComp():
return [x + 10 for x in repslist]
def mapCall():
return list(map((lambda x: x + 10), repslist)) # list in 3.0 only
def genExpr():
return list(x + 10 for x in repslist) # list in 2.6 + 3.0
def genFunc():
def gen():
for x in repslist:
yield x + 10
return list(gen())
...
...
Now the need to call a user-defined function for the map call makes it slower than the for loop statements, despite the fact that the looping statements version is larger in terms of code. On Python 3.0:
C:\misc> c:\python30\python timeseqs.py
3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)]
---------------------------------
forLoop : 2.60754 => [10...10009]
---------------------------------
listComp : 1.57585 => [10...10009]
---------------------------------
mapCall : 3.10276 => [10...10009]
---------------------------------
genExpr : 1.96482 => [10...10009]
---------------------------------
genFunc : 1.95340 => [10...10009]
The Python 2.5 results on a slower machine were again relatively similar in the prior edition, but twice as slow due to test machine differences:
2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit (Intel)]
forStatement => 5.25699996948
listComprehension => 2.68400001526
mapFunction => 5.96900010109
generatorExpression => 3.37400007248
Because the interpreter optimizes so much internally, performance analysis of Python code like this is a very tricky affair. It’s virtually impossible to guess which method will perform the best—the best you can do is time your own code, on your computer, with your version of Python. In this case, all we should say for certain is that on this Python, using a user-defined function in map calls can slow performance by at least a factor of 2, and that list comprehensions run quickest for this test.
As I’ve mentioned before, however, performance should not be your primary concern when writing Python code—the first thing you should do to optimize Python code is to not optimize Python code! Write for readability and simplicity first, then optimize later, if and only if needed. It could very well be that any of the five alternatives is quick enough for the data sets your program needs to process; if so, program clarity should be the chief goal.