Exec is slow since compiling the string and calls to globals() use a lot of time. The last one is most elegant but __getattr__ and __setattr__ are costly. The 'evil hack' solution is good since accessing x and y takes no additional time.

Previous comparison was not completely fair since I could pre-compile fun2 and I used indirect __setattr__. Here is the new one:



>>> import profile >>> a = {'x':1, 'y':2} >>> N = 100000 >>> # solution one: use dictionary directly ... def fun1(d): ... for i in xrange(0,N): ... d['z'] = d['x'] + d['y'] ... >>> # solution two: use exec ... def makeFunction(funcStr, name): ... code = compile(funcStr, name, 'exec') ... def f(d): ... exec code in d ... return f ... >>> def fun2(d): ... myfun = makeFunction('z = x + y', 'myfun') ... for i in xrange(0,N): ... myfun(d) ... del d['__builtins__'] ... ... # solution three: update local dictionary >>> # Note that locals() is NOT d after update() so ... # z = x + y ... # does not set z in d ... def fun3(d): ... exec "locals().update(d)" ... for i in xrange(0,N): ... d['z'] = x + y ... >>> # solution four: use dict wrapper ... # this makes code easier to write and read ... class wrapdict(object): ... """Lazy attribute access to dictionary keys. Will not access ... keys that are not valid attribute names!""" ... def __init__(self, mydict): ... self.__dict__ = mydict ... ... # use wrapper >>> def fun4(d): ... wd = wrapdict(d) ... for i in xrange(0,N): ... wd.z = wd.x + wd.y ... >>> profile.run('fun1(a)') 3 function calls in 0.060 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.060    0.060 <string>:1(?)
        1    0.000    0.000    0.060    0.060 profile:0(fun1(a))
        0    0.000             0.000          profile:0(profiler)
        1    0.060    0.060    0.060    0.060 python-10176FWs.py:2(fun1)


>>> profile.run('fun2(a)') 200004 function calls in 2.130 CPU seconds

   Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 2.130 2.130 <string>:1(?)
100000 0.520 0.000 0.520 0.000 myfun:1(?)
1 0.000 0.000 2.130 2.130 profile:0(fun2(a))
0 0.000 0.000 profile:0(profiler)
1 0.590 0.590 2.130 2.130 python-10176EqB.py:1(fun2)
1 0.000 0.000 0.000 0.000 python-10176Sgy.py:2(makeFunction)
100000 1.020 0.000 1.540 0.000 python-10176Sgy.py:4(f)



>>> profile.run('fun3(a)') 4 function calls (3 primitive calls) in 0.070 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      2/1    0.000    0.000    0.070    0.070 <string>:1(?)
        1    0.000    0.000    0.070    0.070 profile:0(fun3(a))
        0    0.000             0.000          profile:0(profiler)
        1    0.070    0.070    0.070    0.070 python-10176R0H.py:4(fun3)


>>> profile.run('fun4(a)') 4 function calls in 0.100 CPU seconds

   Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.100 0.100 <string>:1(?)
1 0.000 0.000 0.100 0.100 profile:0(fun4(a))
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 python-10176e-N.py:6(__init__)
1 0.100 0.100 0.100 0.100 python-10176rIU.py:1(fun4)



Since

  d['x'] is fast but cumbersome
  exec "z=x+y' is still slow.
  exec "locals().update(d)" is evil
  d.x is elegant and only a little slower than d['x']

I am announcing the winner of the contest: dictwrap! (applause)

Bo

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to