Local variables in Python timeit setup

pystudent picture pystudent · Jul 22, 2015 · Viewed 8.1k times · Source

In all of the places I read about timeit I found only that I can use variables in this way:

s1 = 'abc'
s2 = 'abc'
timeit.timeit('s1==s2', 'from __main__ import s1, s2', number=10**4)

or

s1 = 'abc'
s2 = 'abc'
def func():
    timeit.time('s1==s2', 'from __main__ import s1,s2', number=10**4)

which means that you can also use timeit.timeit in a function as long as the variables are in the main program. I would like to use timeit.timeit with variables that are within the scope it is in, for example:

def func():
    s1 = 'abc'
    s2 = 'abc'
    timeit.timeit(...)

As you can see my question is:

How can I use timeit.timeit with the variables that are in the same scope, when they're both not in the main program?

Answer

Aaron Hall picture Aaron Hall · Jul 22, 2015

I would like to use timeit.timeit with variables that are within the scope it is in.

TLDR:

Use a lambda closure (so called because it closes over the variables in the function):

def func():
    s1 = 'abc'
    s2 = 'abc'
    return timeit.timeit(lambda: s1 == s2)

And I think that's just about what you're asking for.

>>> func()
0.12512516975402832

Explanation

So in the global scope, you want to use the globals, and local scope, locals? On the global scope, locals() returns the same as globals(), so it you ', '.join(locals()) and stick that on the end of 'from __main__ import ', or globals() as they're equivalent on the global scope:

>>> s1 = 'abc'
>>> s2 = 'abc'
>>> timeit.timeit('s1==s2', 'from __main__ import ' + ', '.join(globals()))
0.14271061390928885

You could do this with a function and the globals() too, but you can't use locals():

s1 = 'abc'
s2 = 'abc'
def func():
    return timeit.timeit('s1==s2', 'from __main__ import ' + ', '.join(globals()))

and

>>> func()
0.14236921612231157

but the below doesn't work, because you've got to have access to the variables hidden in the local scope of the function, from the import statement:

def func():
    s1 = 'abc'
    s2 = 'abc'
    return timeit.timeit('s1==s2', 'from __main__ import ' + ', '.join(locals()))

But because you can simply pass the function to timeit, what you can do is this:

def func(s1='abc', s2='abc'):
    s1 == s2

and

>>> timeit.timeit(func)
0.14399981498718262

So that also means, in your func, you can provide timeit a lambda closure:

def func():
    s1 = 'abc'
    s2 = 'abc'
    return timeit.timeit(lambda: s1 == s2)

Or a full function def:

def func():
    s1 = 'abc'
    s2 = 'abc'
    def closure():
        return s1 == s2
    return timeit.timeit(closure)

And I think that's just about what you're asking for.

>>> func()
0.12512516975402832

When they're both not in the main program

If you want to join the globals instead with a setup, from other modules than __main__, use this:

'from ' + __name__ + ' import ' + ', '.join(globals())