[Tutor] Advise...

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Wed Jan 26 20:01:09 CET 2005



On Wed, 26 Jan 2005, Bill Kranec wrote:

> There has been alot of talk on this list about using list comprehensions
> lately, and this could be one of those useful places.  While I don't
> have time to experiment with real code, I would suggest changing your
> function to look like:
>
> steps = [ min_x + i*delta_x for i in range(steps) ]
> totalarea = sum([ eval(func_x)*delta_x for x in steps ])
>
> Since list comprehensions are significantly faster than while loops,
> this could be a big speed boost.
>
> There may be a mistake or two in the above code, but hopefully the idea
> will be helpful.


Calling eval() there in the inner loop might be costly, because Python
needs to do extra work to tokenize and parse the string, every time
through the iteration.  We want to reduce the work done in tight inner
loops like that.

We can do some of that work up front by compiling the code.  Here's some
hacky code to do the compilation up front:

###
>>> def makeFunction(expressionString):
...     compiledExp = compile(expressionString, 'makeFunction', 'eval')
...     def f(x):
...         return eval(compiledExp, {}, {'x' : x})
...     return f
...
###


Some of the stuff there is a bit obscure, but the idea is that we get
Python to parse and compile the expression down once.  Later on, we can
evaluation the compiled code, and that should be faster than evaluating a
string.


Once we have this, we can use it like this:

###
>>> myFunction = makeFunction("3*x*x")
>>> myFunction(0)
0
>>> myFunction(1)
3
>>> myFunction(2)
12
>>> myFunction(3)
27
###

So although makeFunction()'s internals are weird, it shouldn't be too hard
to treat it as a black box.  *grin*



Let's see how this performs against that 3x^2 expression we saw before.
The original approach that calls eval() on the string takes time:

###
>>> def timeIt(f, n=1000):
...     start = time.time()
...     for i in xrange(n):
...         f(i)
...     end = time.time()
...     return end - start
...
>>> def myFunctionOriginal(x):
...     return eval("3*x*x")
...
>>> timeIt(myFunctionOriginal)
0.036462068557739258
###


The precompiled expression can work more quickly:

###
>>> timeIt(myFunction)
0.0050611495971679688
###


And we should still get the same results:

###
>>> for i in range(2000):
...     assert myFunction(i) == myFunctionOriginal(i)
...
>>>
###


I hope this helps!



More information about the Tutor mailing list