Python:加速重复执行的eval语句的方法?

Python:加速重复执行的eval语句的方法?,python,eval,Python,Eval,在我的代码中,我使用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() f

在我的代码中,我使用
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) # <-- option ONE
    #result[index] = math.sin(v['x']) * v['y'] # <-- option TWO
导入数学
随机输入
结果\u计数=100000
expression=“math.sin(v['x'])*v['y']”
变量=dict()
变量['x']=[range中的u的random.random()值(结果计数)]
变量['y']=[random.random()表示xrange中的u(结果_计数)]
#优化此线以下的任何内容
结果=[0]*结果\u计数
打印“计算给定表达式的%d个实例:'%result\u count”
打印表达式
v=dict()
对于xrange中的索引(结果计数):
对于变量.keys()中的名称:
v[name]=变量[name][index]

result[index]=eval(expression)#您可以通过使用Python 2或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, '<string>', 'eval')

In [6]: %timeit eval(compiled)
1000000 loops, best of 3: 823 ns per loop
[1]中的
:导入数学,编译器
在[2]中:v={x':2,'y':4}
在[3]中:expression=“math.sin(v['x'])*v['y']”
在[4]中:%timeit eval(表达式)
10000圈,最好3圈:19.5美元/圈
在[5]中:compiled=compiler.compile(表达式“”,“eval”)
在[6]:%timeit评估(已编译)
1000000个循环,最佳3个:每个循环823纳秒

只需确保只编译一次(在循环之外)。如注释中所述,当对用户提交的字符串使用
eval
时,请确保您非常小心地接受内容。

您还可以欺骗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)

我认为你优化了错误的结局。如果你想对许多数字执行相同的操作,你应该考虑使用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

速度没有我预期的那么快,但仍然很显著。

在本文中查看一些eval的替代方案,以及一些远离eval的好理由。如果用户键入
导入操作系统;操作系统(“rm-rf/”)
?您需要编写一个解析器来解释输入字符串,并且只识别您期望的内容:
sin
cos
log
,等等。如果输入的内容不起作用,则抛出错误。如果您不这样做可能会很糟糕。如果用户想要“rm-rf/”或“:”({:::&}:“他可以在shell中而不是在Python中完成。这比F.J.的响应速度明显提高,这已经是一个很大的速度提升。我想这个技巧相当于在eval之前使用
compile
,因为当你运行它时,你会得到
最慢的运行时间比最快的运行时间长17.90倍。这可能意味着一个中间结果正在被缓存
。谁是“F.J.”?我在这里无权访问
numpy
。但我同意,这可能会加快速度。如果没有第三方库,我通常会反对依赖它。compiler.compile是用python3编译的