Local variables in Python timeit setup [duplicate]
Asked Answered
T

2

18

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?

Thimbleweed answered 22/7, 2015 at 14:17 Comment(0)
M
24

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())
Morganica answered 22/7, 2015 at 19:52 Comment(1)
Thank you very much for you answer! It was very helpful.Thimbleweed
T
4

As jonrsharpe has explained, there's no (straightforward) way for a function running on the timeit scope to access something outside its scope that is not a global.

You should consider rewriting your function to take the variables it needs to use as arguments - using globals is generally considered a bad practice that leads to a lot of problems.

In order to provide the arguments to timeit.timeit, you can use a partial function:

from functools import partial

def func(s1,s2):
    pass

timeit.timeit( partial( func, s1='bla', s2='bla' ) )
Thunderbolt answered 22/7, 2015 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.