我正在阅读David Beazley的Python参考书,他指出:
例如,如果你正在表演 很多平方根操作,它是 更快地使用'来自数学导入sqrt' 和'sqrt(x)'而不是打字 'math.sqrt(X)'
和
涉及大量使用的计算 方法或模块查找,它是 几乎总是更好地消除 通过放置属性查找 你想要执行的操作 首先是局部变量。
我决定尝试一下:
第一()
def first():
from collections import defaultdict
x = defaultdict(list)
第二()
def second():
import collections
x = collections.defaultdict(list)
结果是:
2.15461492538
1.39850616455
这些优化对我来说可能无关紧要。但我很好奇为什么Beazley所写的与之相反的结果是真实的。请注意,存在1秒的差异,这是重要的,因为任务是微不足道的。
为什么会这样?
更新:
我得到的时间如下:
print timeit('first()', 'from __main__ import first');
print timeit('second()', 'from __main__ import second');
答案 0 :(得分:6)
from collections import defaultdict
和import collections
应该在迭代的时序循环之外,因为你不会重复这样做。
我想from
语法必须做更多import
语法的工作。
使用此测试代码:
#!/usr/bin/env python
import timeit
from collections import defaultdict
import collections
def first():
from collections import defaultdict
x = defaultdict(list)
def firstwithout():
x = defaultdict(list)
def second():
import collections
x = collections.defaultdict(list)
def secondwithout():
x = collections.defaultdict(list)
print "first with import",timeit.timeit('first()', 'from __main__ import first');
print "second with import",timeit.timeit('second()', 'from __main__ import second');
print "first without import",timeit.timeit('firstwithout()', 'from __main__ import firstwithout');
print "second without import",timeit.timeit('secondwithout()', 'from __main__ import secondwithout');
我得到了结果:
first with import 1.61359190941
second with import 1.02904295921
first without import 0.344709157944
second without import 0.449721097946
显示重复进口的成本。
答案 1 :(得分:4)
我会在first(.)
和second(.)
之间得到相似的比率,唯一不同的是时间是微秒级别。
我不认为你的时间衡量任何有用的东西。试着找出更好的测试用例!
<强>更新强>
FWIW,这是一些支持David Beazley的测试。
import math
from math import sqrt
def first(n= 1000):
for k in xrange(n):
x= math.sqrt(9)
def second(n= 1000):
for k in xrange(n):
x= sqrt(9)
In []: %timeit first()
1000 loops, best of 3: 266 us per loop
In [: %timeit second()
1000 loops, best of 3: 221 us per loop
In []: 266./ 221
Out[]: 1.2036199095022624
所以first()
比second()
慢了约20%。
答案 2 :(得分:1)
我的猜测,你的测试是有偏见的,第二个实现从第一个已加载模块的实现中获益,或者仅仅是从最近加载它。
你尝试了多少次?你切换了订单等等。
答案 3 :(得分:1)
first()
不保存任何内容,因为仍然必须访问模块才能导入名称。
另外,你没有给出你的计时方法,但是给出了函数名称,似乎first()
执行初始导入,由于必须编译和执行模块,因此总是比后续导入更长。
答案 4 :(得分:1)
还有读取/理解源代码的效率问题。这是一个真实的实例(来自stackoverflow question的代码)
原件:
import math
def midpoint(p1, p2):
lat1, lat2 = math.radians(p1[0]), math.radians(p2[0])
lon1, lon2 = math.radians(p1[1]), math.radians(p2[1])
dlon = lon2 - lon1
dx = math.cos(lat2) * math.cos(dlon)
dy = math.cos(lat2) * math.sin(dlon)
lat3 = math.atan2(math.sin(lat1) + math.sin(lat2), math.sqrt((math.cos(lat1) + dx) * (math.cos(lat1) + dx) + dy * dy))
lon3 = lon1 + math.atan2(dy, math.cos(lat1) + dx)
return(math.degrees(lat3), math.degrees(lon3))
替代:
from math import radians, degrees, sin, cos, atan2, sqrt
def midpoint(p1, p2):
lat1, lat2 = radians(p1[0]), radians(p2[0])
lon1, lon2 = radians(p1[1]), radians(p2[1])
dlon = lon2 - lon1
dx = cos(lat2) * cos(dlon)
dy = cos(lat2) * sin(dlon)
lat3 = atan2(sin(lat1) + sin(lat2), sqrt((cos(lat1) + dx) * (cos(lat1) + dx) + dy * dy))
lon3 = lon1 + atan2(dy, cos(lat1) + dx)
return(degrees(lat3), degrees(lon3))
答案 5 :(得分:0)
照常编写代码,导入模块并将其模块和常量引用为module.attribute
。然后,使用decorator for binding constants为函数添加前缀,或使用下面的bind_all_modules
函数绑定程序中的所有模块:
def bind_all_modules():
from sys import modules
from types import ModuleType
for name, module in modules.iteritems():
if isinstance(module, ModuleType):
bind_all(module)
def bind_all(mc, builtin_only=False, stoplist=[], verbose=False):
"""Recursively apply constant binding to functions in a module or class.
Use as the last line of the module (after everything is defined, but
before test code). In modules that need modifiable globals, set
builtin_only to True.
"""
try:
d = vars(mc)
except TypeError:
return
for k, v in d.items():
if type(v) is FunctionType:
newv = _make_constants(v, builtin_only, stoplist, verbose)
try: setattr(mc, k, newv)
except AttributeError: pass
elif type(v) in (type, ClassType):
bind_all(v, builtin_only, stoplist, verbose)