在我的代码中,我使用eval来计算用户给定的字符串表达式。是否有方法编译或以其他方式加速此语句?
import math
import random
result_count = 100000
expression ="math.sin(v['x']) * v['y']"
variable = dict()
variable['x'] = [random.random() for _ in xrange(result_count)]
variable['y'] = [random.random() for _ in xrange(result_count)]
# optimize anything below this line
result = [0] * result_count
print 'Evaluating %d instances of the given expression:' % result_count
print expression
v = dict()
for index in xrange(result_count):
for name in variable.keys():
v[name] = variable[name][index]
result[index] = eval(expression) #
#result[index] = math.sin(v['x']) * v['y'] #
对于快速比较选项,一个在我的机器上需要2.019秒,而选项二只需要0.218秒。当然,Python有一种在不硬编码表达式的情况下实现这一点的方法。
在这个post stackoverflow.com/questions/1832940中查看一些eval的替代方法,以及一些远离它的好理由。
如果用户键入import os;os.system("rm -rf /"),会怎么样?您需要编写一个解析器来解释输入字符串,并且只识别您期望的内容:sin、cos、log等。如果输入的内容不起作用,则抛出一个错误。如果你不这样做可能会很糟糕。
如果用户想要"rm-rf/"或":()::&;":"他可以在shell中而不是在python中执行。
您还可以使用python:
expression ="math.sin(v['x']) * v['y']"
exp_as_func = eval('lambda: ' + expression)
号
然后像这样使用:
exp_as_func()
速度测试:
In [17]: %timeit eval(expression)
10000 loops, best of 3: 25.8 us per loop
In [18]: %timeit exp_as_func()
1000000 loops, best of 3: 541 ns per loop
。
附带说明,如果v不是全局的,您可以这样创建lambda:
exp_as_func = eval('lambda v: ' + expression)
并称之为:
exp_as_func(my_v)
。
与F.J.的反应相比,这是一个显著的速度提升,而这已经是一个巨大的速度提升。
我猜这个技巧相当于在评估之前使用compile,因为当你运行它时,你会得到The slowest run took 17.90 times longer than the fastest. This could mean that an intermediate result is being cached。
通过使用compiler.compile()for python 2或compile()for python 3预先编译表达式,可以避免开销:
In [1]: import math, compiler
In [2]: v = {'x': 2, 'y': 4}
In [3]: expression ="math.sin(v['x']) * v['y']"
In [4]: %timeit eval(expression)
10000 loops, best of 3: 19.5 us per loop
In [5]: compiled = compiler.compile(expression, '', 'eval')
In [6]: %timeit eval(compiled)
1000000 loops, best of 3: 823 ns per loop
只需确保只编译一次(在循环之外)。正如注释中所提到的,当对用户提交的字符串使用eval时,请确保对您接受的内容非常小心。
这是一个相当大的收获…
compiler.compile在python3中编译
我认为你在优化错误的结局。如果要对许多数字执行相同的操作,应考虑使用numpy:
import numpy
import time
import math
import random
result_count = 100000
expression ="sin(x) * y"
namespace = dict(
x=numpy.array(
[random.random() for _ in xrange(result_count)]),
y=numpy.array(
[random.random() for _ in xrange(result_count)]),
sin=numpy.sin,
)
print ('Evaluating %d instances '
'of the given expression:') % result_count
print expression
start = time.time()
result = eval(expression, namespace)
numpy_time = time.time() - start
print"With numpy:", numpy_time
assert len(result) == result_count
assert all(math.sin(a) * b == c for a, b, c in
zip(namespace["x"], namespace["y"], result))
。
为了让您了解可能获得的收益,我添加了一个使用通用python和lambda技巧的变体:
from math import sin
from itertools import izip
start = time.time()
f = eval("lambda:" + expression)
result = [f() for x, y in izip(namespace["x"], namespace["y"])]
generic_time = time.time() - start
print"Generic python:", generic_time
print"Ratio:", (generic_time / numpy_time)
以下是我的老化机的结果:
$ python speedup_eval.py
Evaluating 100000 instances of the given expression:
sin(x) * y
With numpy: 0.006098985672
Generic python: 0.270224094391
Ratio: 44.3063992807
。
速度没有我预期的那么快,但仍然很重要。
我不能在这里访问numpy。但我同意,这可能会加快速度。如果没有第三方图书馆,我一般不会依赖它。