Using a dict as if it were a module namespace

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sun Jan 27 02:45:29 EST 2008


I have a problem which I think could be solved by using a dict as a 
namespace, in a similar way that exec and eval do.

When using the timeit module, it is very inconvenient to have to define 
functions as strings. A good alternative is to create the function as 
normal, and import it:

def myfunc(x, y):
    return x+y

timeit.Timer("myfunc(59, 60)", "from __main__ import myfunc").timeit()


Not only is this an easy idiom to follow, but myfunc can live in another 
module: just replace __main__ with the module name.

Now, I'm trying to build a suite of tests to use with timeit. I have a 
bunch of tests which I've created as dicts:

test_suite= [dict(x=59, y=60), dict(x=-1, y=-2)]

What I *think* I want to do is use the from ... import idiom to grab 
arguments from the dicts as if they were modules, but this doesn't work:

expr = "myfunc(x, y)"
for test in test_suite:
    setup = "from __main__ import myfunc; from test import x, y"
    t = timeit.Timer(expr, setup).timeit()


Even if the Timer could see test, it is not a module and you can't import 
from it. Naturally.


Alternatives that I have found:

(1) Import the test and grab the values needed from it:

    setup = """from __main__ import myfunc, test
x, y = test['x'], test['y']"""


I don't like this one. It doesn't seem very elegant to me, and it gets 
unwieldy as the complexity increases. Every item I need from test has to 
be named twice, violating the principle Don't Repeat Yourself. If the 
tests change, the setup string has to be explicitly changed also. 


(2) Mess with the global namespace:

    globals().update(t)
    setup = "from __main__ import myfunc"

I don't like this one. It looks hackish, and I worry about conflicts and 
side-effects. If it works (and I haven't tested it) it relies on an 
implementation detail of timeit.Timer.__init__, namely the line
"exec code in globals(), ns". Worst of all, it pollutes or even mangles 
the global namespace of the calling code, not the code being tested.


(3) Explicitly pass a namespace dict to the Timer class, possibly even 
getting rid of setup altogether:

    test['myfunc'] = myfunc
    t = timeit.Timer(expr, '', ns=test).timeit()

This would be the most elegant solution, but at this time it is 
completely hypothetical. Timer does not have that functionality.


(4) Dump the test data straight into the setup string:

    setup = "from __main__ import myfunc; x = %(x)s; y = %(y)s" % t

Again, unwieldy and against DRY. The additional disadvantage is that 
there are many types of test data that can't be converted to and from 
strings like that.


What do others think? Have I missed something? What other alternatives 
are there?



-- 
Steven



More information about the Python-list mailing list