1
import time
class Timeit(object):
    def __init__(self, func):
        self._wrapped = func
    def __call__(self, *args, **kws):
        start_time = time.time()
        result = self._wrapped(*args, **kws)
        print("elapsed time is %s " % (time.time() - start_time))
        return result

I write a class decorator like this , however , when I use it to a instance method, It does not work.

class A(object):
    @Timeit
    def func(self):
        time.sleep(1)
        return 'invoking method func'
 if __name__ == '__main__':
    a = A()
    a.func()  # Boom!

could anyone explain me why? It works well in normal method.

yunji
  • 131
  • 1
  • 1
  • 6
  • That's not a class decorator, that's just a class that is being used as a decorator. – Ignacio Vazquez-Abrams Dec 08 '16 at 05:24
  • 1
    Check this out. http://stackoverflow.com/questions/9416947/python-class-based-decorator-with-parameters-that-can-decorate-a-method-or-a-fun – Prajwal Dec 08 '16 at 05:29
  • Rather than try and create a decorator yourself that will work properly for both a function or a instance method, use a package that does all the hard work for you and does it correctly. Consider having a look at wrapt. http://wrapt.readthedocs.io – Graham Dumpleton Dec 08 '16 at 05:36

1 Answers1

0

The problem is that your @Timeit decorator means that A.func is an object, not a function. Therefore, when you evaluate:

a.func()

the special Python handling of class methods that turns a function into a bound method with the implicit self argument doesn't take effect. If you were to run:

a.func(a)

to supply the missing argument, it would work fine, but obviously that's not what you want.

You need to ensure that your wrapper returns a function, not an object. The simplest way is to abandon implementing Timeit as a class and replace your code with:

import time
from functools import wraps

def Timeit(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        start_time = time.time()
        result = f(*args, **kwds)
        print("elapsed time is %s " % (time.time() - start_time))
        return result
    return wrapper

class A(object):
    @Timeit
    def func(self):
        time.sleep(1)
        return 'invoking method func'

if __name__ == '__main__':
    a = A()
    a.func()  # works fine now
K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71