timeit.timeit method error "expected an indented block"

684 views Asked by At

As I am learning Python 3.5 and I wanted to start comparing time on different code. I have tried the timeit.timeit module on a couple other simple statements and got it to work.

I can't get it to work on the below code and get the error below:

Traceback (most recent call last):
File "C:\Users\ASUS\Desktop\pythoncode\class_gen.py", line 10, in <module>
    print(timeit.timeit(",".join(["#"+i+j for i in listTags for j in listTags if i != j])))
  File "C:\Python35\lib\timeit.py", line 213, in timeit
    return Timer(stmt, setup, timer, globals).timeit(number)
  File "C:\Python35\lib\timeit.py", line 133, in __init__
    code = compile(src, dummy_src_name, "exec")
  File "<timeit-src>", line 7
    _t1 = _timer()
      ^
IndentationError: expected an indented block"""

The code I am using is:

import itertools
import timeit

listTags = [ "TOT" , "WBA", "BUR", "SOU"]

print(timeit.timeit(",".join(["#" + "".join(t) for t in itertools.permutations(listTags, 2)]))

print(timeit.timeit(",".join(["#"+i+j for i in listTags for j in listTags if i != j])))

I have tried it with and without the number=number keyword to no avail.

2

There are 2 answers

1
Jean-François Fabre On BEST ANSWER

You have to pass a python code as string in the timeit call.

You were passing the evaluated value of your expression (#TOTWBA,#TOTBUR,#TOTSOU,#WBATOT,#WBABUR,#WBASOU,#BURTOT,#BURWBA,#BURSOU,#SOUTOT,#SOUWBA,#SOUBUR) to be executed.

Moreover, you have to pass listTags literally, or using setup expression, as timeit runs in a different interpreter: it's not aware of your previously defined variables.

I rewrote the first timeit call using single quotes to protect your statement and added a setup call to define the listTags variable.

import itertools
import timeit

print(timeit.timeit('",".join(["#" + "".join(t) for t in itertools.permutations(listTags, 2)])',setup='listTags = [ "TOT" , "WBA", "BUR", "SOU"]'))

I get a time as a result: 5.2231671576142285: it's working

2
Dimitris Fasarakis Hilliard On

@Jean is correct, you need to pass it as a string to timeit (wrap it up in single quotes ') in order for it to work correctly. You also need to pass the appropriate setup for name look-ups to succeed.

Still, it is interesting to note why this happens, though.

When you try and timeit with:

print(timeit.timeit(",".join(["#" + "".join(t) for t in itertools.permutations(listTags, 2)]))

you should realize that the argument gets evaluated. timeit is eventually called with the argument:

timeit.timeit('#TOTWBA,#TOTBUR,#TOTSOU,#WBATOT,#WBABUR,#WBASOU,#BURTOT,#BURWBA,#BURSOU,#SOUTOT,#SOUWBA,#SOUBUR')

Inside timeit, this is formatted into a default template that gets timed; it's easy to re-create that template to see what Python tries to execute:

template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        {stmt}
    _t1 = _timer()
    return _t1 - _t0
"""  
src = template.format(stmt=",".join(["#"+i+j for i in listTags for j in listTags if i != j]), setup='', init='')

So, what gets executed is:

>>> print(src)
def inner(_it, _timer):

    _t0 = _timer()
    for _i in _it:
        #TOTWBA,#TOTBUR,#TOTSOU,#WBATOT,#WBABUR,#WBASOU,#BURTOT,#BURWBA,#BURSOU,#SOUTOT,#SOUWBA,#SOUBUR
    _t1 = _timer()
    return _t1 - _t0

Which, if you try and execute normally in your REPL, would result in an IndentationError since the for loop only has a comment in its body (which gets removed during parsing). Executing:

for i in [1, 2]:
    # foo
print('fooing')

Results in:

  File "<ipython-input-58-634beff715a1>", line 3
    print('fooing')
        ^
IndentationError: expected an indented block