Python - Timeit within a class
Asked Answered
D

5

9

I'm having some real trouble with timing a function from within an instance of a class. I'm not sure I'm going about it the right way (never used timeIt before) and I tried a few variations of the second argument importing things, but no luck. Here's a silly example of what I'm doing:

import timeit

class TimedClass():
    def __init__(self):
        self.x = 13
        self.y = 15
        t = timeit.Timer("self.square(self.x, self.y)")
        try:
            t.timeit()
        except:
            t.print_exc()

    def square(self, _x, _y):
        print _x**_y

myTimedClass = TimedClass()

Which, when ran, complains about self.

Traceback (most recent call last):
  File "timeItTest.py", line 9, in __init__
    t.timeit()
  File "C:\Python26\lib\timeit.py", line 193, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
    self.square(self.x, self.y)
NameError: global name 'self' is not defined

This has to do with TimeIt creating a little virtual environment to run the function in but what do I have to pass to the second argument to make it all happy?

Denude answered 31/8, 2010 at 12:46 Comment(2)
Do you have any specific reason why you want to use timeit within the init of the class? You should just time that function call from the "outside": t = timeit.Timer("myTimedClass.square(x, y)") Btw "myTimedClass" is not an inspired name, because that is an instance of TimedClass, not a class.Consols
In my real program, I need to read a bunch of data from an external file to set up the object. The reading code I separated from the init to another function in case I need to update the object again later. Since I'm running a bunch of instances and they do their own file loading, I want them all to time themselves too. In the simple example I posted above, of course I could time it from outside, but that doesn't help with my real project.Denude
A
10

if you're willing to consider alternatives to timeit, i recently found the stopwatch timer utility which might be useful in your case. it's really simple and intuitive, too:

import stopwatch

class TimedClass():

    def __init__(self):
        t = stopwatch.Timer()
        # do stuff here
        t.stop()
        print t.elapsed
Alonzoaloof answered 31/8, 2010 at 13:39 Comment(1)
Much simpler than figuring out timeit. Python's got a good breakdown of all the properties of timeit, but the explanation and examples of how to use it to profile code are confusing.Chapatti
E
9

Why do you want the timing inside the class being timed itself? If you take the timing out of the class, you can just pass a reference. I.e.

import timeit

class TimedClass():
    def __init__(self):
        self.x = 13
        self.y = 15

    def square(self, _x, _y):
        print _x**_y

myTimedClass = TimedClass()
timeit.Timer(myTImedClass.square).timeit()

(of course the class itself is redundant, I assume you have a complexer use-case where a simple method is not sufficient).

In general, just pass a callable that has all setup contained/configured. If you want to pass strings to be timed they should contain all necessary setup inside them, i.e.

timeit.Timer("[str(x) for x in range(100)]").timeit()

If you really, really need the timing inside the class, wrap the call in a local method, i.e.

def __init__(self, ..):
    def timewrapper():
        return self.multiply(self.x, self.y)

    timeit.Timer(timewrapper)
Everyman answered 31/8, 2010 at 13:46 Comment(1)
Comprehensive answer. I'll be looking at using the timewrapper. Thanks!Denude
I
9

To address your initial error, you can use timeit within a class with parameters like this:

 t = timeit.Timer(lambda: self.square(self.x, self.y)).timeit()
Isiahisiahi answered 30/3, 2016 at 10:51 Comment(0)
T
1

Just pass self through the new globals parameter of timeit.Timer() (added in version 3.5):

import timeit

class TimedClass():
    def __init__(self):
        self.x = 13
        self.y = 15
        my_globals = globals()
        my_globals.update({'self':self})
        t = timeit.Timer(stmt="self.square(self.x, self.y)", globals=my_globals)
        try:
            t.timeit()
        except:
            t.print_exc()

    def square(self, _x, _y):
        print (_x**_y)

myTimedClass = TimedClass()
Till answered 1/12, 2022 at 15:28 Comment(0)
A
0

(posting as an answer because the code markup does not work in a comment)

I would add a try/finally closure for additional safety:

class TimedClass():
  def __init__(self):
    t = stopwatch.Timer()
    try:
      # do stuff here, you can even use return "foo" here and throw exceptions
    finally:
      t.stop()
      print t.elapsed
Aquino answered 13/11, 2014 at 8:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.