diff -r acb30ed7eceb Doc/library/timeit.rst --- a/Doc/library/timeit.rst Wed Aug 13 09:36:06 2014 +0300 +++ b/Doc/library/timeit.rst Mon Aug 18 17:05:08 2014 -0400 @@ -59,7 +59,7 @@ The module defines three convenience functions and a public class: -.. function:: timeit(stmt='pass', setup='pass', timer=, number=1000000) +.. function:: timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None) Create a :class:`Timer` instance with the given statement, *setup* code and *timer* function and run its :meth:`.timeit` method with *number* executions. @@ -71,7 +71,7 @@ It will instead return the data specified by your return statement. -.. function:: repeat(stmt='pass', setup='pass', timer=, repeat=3, number=1000000) +.. function:: repeat(stmt='pass', setup='pass', timer=, repeat=3, number=1000000, globals=None) Create a :class:`Timer` instance with the given statement, *setup* code and *timer* function and run its :meth:`.repeat` method with the given *repeat* @@ -86,7 +86,7 @@ :func:`time.perf_counter` is now the default timer. -.. class:: Timer(stmt='pass', setup='pass', timer=) +.. class:: Timer(stmt='pass', setup='pass', timer=, globals=None) Class for timing execution speed of small code snippets. @@ -94,7 +94,9 @@ for setup, and a timer function. Both statements default to ``'pass'``; the timer function is platform-dependent (see the module doc string). *stmt* and *setup* may also contain multiple statements separated by ``;`` - or newlines, as long as they don't contain multi-line string literals. + or newlines, as long as they don't contain multi-line string literals. The + statement will by default be executed within timeit's namespace; this behavior + can be controlled by passing a namespace to *globals*. To measure the execution time of the first statement, use the :meth:`.timeit` method. The :meth:`.repeat` method is a convenience to call :meth:`.timeit` @@ -324,3 +326,17 @@ if __name__ == '__main__': import timeit print(timeit.timeit("test()", setup="from __main__ import test")) + +Another option is to specify the *globals* parameter which will cause the code +to be executed within your current global namespace. This can be more convenient +than individually specifying imports:: + + def f(x): + return x**2 + def g(x): + return x**4 + def h(x): + return x**8 + + import timeit + print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals())) diff -r acb30ed7eceb Lib/test/test_timeit.py --- a/Lib/test/test_timeit.py Wed Aug 13 09:36:06 2014 +0300 +++ b/Lib/test/test_timeit.py Mon Aug 18 17:05:08 2014 -0400 @@ -86,9 +86,10 @@ def fake_callable_stmt(self): self.fake_timer.inc() - def timeit(self, stmt, setup, number=None): + def timeit(self, stmt, setup, number=None, globals=None): self.fake_timer = FakeTimer() - t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) + t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer, + globals=globals) kwargs = {} if number is None: number = DEFAULT_NUMBER @@ -127,6 +128,17 @@ timer=FakeTimer()) self.assertEqual(delta_time, 0) + def test_timeit_globals_args(self): + global _global_timer + _global_timer = FakeTimer() + t = timeit.Timer(stmt='_global_timer.inc()', timer=_global_timer) + self.assertRaises(NameError, t.timeit, number=3) + timeit.timeit(stmt='_global_timer.inc()', timer=_global_timer, + globals=globals(), number=3) + local_timer = FakeTimer() + timeit.timeit(stmt='local_timer.inc()', timer=local_timer, + globals=locals(), number=3) + def repeat(self, stmt, setup, repeat=None, number=None): self.fake_timer = FakeTimer() t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) diff -r acb30ed7eceb Lib/timeit.py --- a/Lib/timeit.py Wed Aug 13 09:36:06 2014 +0300 +++ b/Lib/timeit.py Mon Aug 18 17:05:08 2014 -0400 @@ -60,6 +60,8 @@ default_repeat = 3 default_timer = time.perf_counter +_globals = globals + # Don't change the indentation of the template; the reindent() calls # in Timer.__init__() depend on setup being indented 4 spaces and stmt # being indented 8 spaces. @@ -94,7 +96,9 @@ The constructor takes a statement to be timed, an additional statement used for setup, and a timer function. Both statements default to 'pass'; the timer function is platform-dependent (see - module doc string). + module doc string). If 'globals' is specified, the code will be + executed within that namespace (as opposed to inside timeit's + namespace). To measure the execution time of the first statement, use the timeit() method. The repeat() method is a convenience to call @@ -104,10 +108,12 @@ multi-line string literals. """ - def __init__(self, stmt="pass", setup="pass", timer=default_timer): + def __init__(self, stmt="pass", setup="pass", timer=default_timer, + globals=None): """Constructor. See class doc string.""" self.timer = timer - ns = {} + local_ns = {} + global_ns = _globals() if globals is None else globals if isinstance(stmt, str): stmt = reindent(stmt, 8) if isinstance(setup, str): @@ -115,19 +121,19 @@ src = template.format(stmt=stmt, setup=setup) elif callable(setup): src = template.format(stmt=stmt, setup='_setup()') - ns['_setup'] = setup + local_ns['_setup'] = setup else: raise ValueError("setup is neither a string nor callable") - self.src = src # Save for traceback display + self.src = src # Save for traceback display code = compile(src, dummy_src_name, "exec") - exec(code, globals(), ns) - self.inner = ns["inner"] + exec(code, global_ns, local_ns) + self.inner = local_ns["inner"] elif callable(stmt): self.src = None if isinstance(setup, str): _setup = setup def setup(): - exec(_setup, globals(), ns) + exec(_setup, global_ns, local_ns) elif not callable(setup): raise ValueError("setup is neither a string nor callable") self.inner = _template_func(setup, stmt) @@ -208,14 +214,14 @@ return r def timeit(stmt="pass", setup="pass", timer=default_timer, - number=default_number): + number=default_number, globals=None): """Convenience function to create Timer object and call timeit method.""" - return Timer(stmt, setup, timer).timeit(number) + return Timer(stmt, setup, timer, globals).timeit(number) def repeat(stmt="pass", setup="pass", timer=default_timer, - repeat=default_repeat, number=default_number): + repeat=default_repeat, number=default_number, globals=None): """Convenience function to create Timer object and call repeat method.""" - return Timer(stmt, setup, timer).repeat(repeat, number) + return Timer(stmt, setup, timer, globals).repeat(repeat, number) def main(args=None, *, _wrap_timer=None): """Main program, used when run as a script.