>>> 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 fun2(d): ... for i in xrange(0,N): ... exec 'z = x + y' in globals(), d ... >>> # 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): ... object.__setattr__(self, "mydict",mydict) ... def __getattr__(self, attrname): ... return self.mydict[attrname] ... def __setattr__(self, attrname, value): ... self.mydict[attrname] = value ... >>> # 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.070 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.010 0.010 0.070 0.070 profile:0(fun1(a)) 0 0.000 0.000 profile:0(profiler) 1 0.060 0.060 0.060 0.060 python-4645vcY.py:2(fun1)
>>> profile.run('fun2(a)') 100003 function calls (3 primitive calls) in 5.890 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function) 100001/1 0.440 0.000 5.890 5.890 <string>:1(?) 1 0.000 0.000 5.890 5.890 profile:0(fun2(a)) 0 0.000 0.000 profile:0(profiler) 1 5.450 5.450 5.890 5.890 python-46458me.py:2(fun2)
>>> profile.run('fun3(a)') 4 function calls (3 primitive calls) in 0.060 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function) 2/1 0.000 0.000 0.060 0.060 <string>:1(?) 1 0.000 0.000 0.060 0.060 profile:0(fun3(a)) 0 0.000 0.000 profile:0(profiler) 1 0.060 0.060 0.060 0.060 python-4645Jxk.py:5(fun3)
>>> profile.run('fun4(a)') 300004 function calls in 3.910 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 3.910 3.910 <string>:1(?)
1 0.000 0.000 3.910 3.910 profile:0(fun4(a))
0 0.000 0.000 profile:0(profiler)
100000 0.530 0.000 0.530 0.000 python-4645W7q.py:10(__setattr__)
1 0.000 0.000 0.000 0.000 python-4645W7q.py:6(__init__)
200000 0.960 0.000 0.960 0.000 python-4645W7q.py:8(__getattr__)
1 2.420 2.420 3.910 3.910 python-4645jFx.py:1(fun4)
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.
I guess I will go with solution 3. It is evil but it is most close to my original intention. It leads to most readable code (except for the first line to do the magic and the last line to return result) and fastest performance.
Thank again for everyone's help. I have learned a lot from the posts, especially the wrapdict class.
Bo -- http://mail.python.org/mailman/listinfo/python-list