Using a dict as if it were a module namespace

thebjorn BjornSteinarFjeldPettersen at gmail.com
Sun Jan 27 03:46:14 EST 2008


On Jan 27, 8:45 am, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
> 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

You might have lost me, but wouldn't it be easier to do some variation
on this

test_suite = [
    '(x=59, y=60)',  # either have strings here...
    '(x=-1, y=-2)',
    ]

for test in test_suite:
    # ... or convert the dicts to appropriate strings here...
    expr = 'myfunc' + test
    t = timeit.Timer(expr, 'from __main__ import myfunc').timeit()
    ...

-- bjorn



More information about the Python-list mailing list