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