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

dev*_*vtk 17 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() 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
Run Code Online (Sandbox Code Playgroud)

对于快速比较选项,ONE在我的机器上需要2.019秒,而选项TWO仅需0.218秒.当然,Python有一种方法可以做到这一点,而无需对表达式进行硬编码.

Oha*_*had 23

你也可以欺骗python:

expression = "math.sin(v['x']) * v['y']"
exp_as_func = eval('lambda: ' + expression)
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

exp_as_func()
Run Code Online (Sandbox Code Playgroud)

速度测试:

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
Run Code Online (Sandbox Code Playgroud)

作为旁注,如果v不是全局的,您可以像这样创建lambda:

exp_as_func = eval('lambda v: ' + expression)
Run Code Online (Sandbox Code Playgroud)

并称之为:

exp_as_func(my_v)
Run Code Online (Sandbox Code Playgroud)

  • 与FJ的反应相比,这显着提高了速度,这已经是一个很大的速度提升. (2认同)
  • “FJ”是谁? (2认同)

And*_*ark 15

您可以使用compiler.compile()以下方法预先编译表达式来避免开销:

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
Run Code Online (Sandbox Code Playgroud)

只需确保只编译一次(在循环之外).正如评论中所提到的,当compile()在用户提交的字符串上使用时,请确保您对所接受的内容非常小心.

  • compiler.compile是在python3中编译的 (3认同)

Pet*_*ten 5

我认为你正在优化错误的结局.如果你想对很多数字执行相同的操作,你应该考虑使用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))
Run Code Online (Sandbox Code Playgroud)

为了让您了解可能的增益,我使用泛型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)
Run Code Online (Sandbox Code Playgroud)

以下是老化机的结果:

$ 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
Run Code Online (Sandbox Code Playgroud)

加速并不像我预期的那么高,但仍然很重要.