Decorator question: prefer class, but only function works

Ian Kelly ian.g.kelly at gmail.com
Thu Nov 10 17:35:14 EST 2011


On Thu, Nov 10, 2011 at 2:52 PM, Russell E. Owen <rowen at uw.edu> wrote:
> I am trying to write a decorator that times an instance method and
> writes the results to a class member variable. For example:
>
> def timeMethod(func):
>    def wrapper(self, *args, **keyArgs):
>        t1 = time.time()
>        res = func(self, *args, **keyArgs)
>        duration = time.time() - t1
>        self.timings[func.__name__] = duration
>        return res
>    return wrapper
>
> This works, but I'm not very happy with the way self.timings is obtained.

What do you feel is wrong with it?  You probably should use
sum(os.times()[:2]) instead, which (assuming your script is
single-threaded) will more accurately count the actual CPU time spent
in the function rather than real time, which could be quite different
if the CPU is busy.

Also, why do you need this?  If you're just trying to evaluate the
speed of your code, you should consider using a proper profiler or the
timeit module.  The former will tell you how much time is spent in
each function, while the latter runs the code a large number of times
in a loop, which gives you better precision for quick methods.

> I first tried to write this as a class (for readability), and this did
> NOT work:
>
> class timeMethod(object):
>    def __init__(self, func):
>        self.func = func
>    def __call__(self, *args, **keyArgs):
>        t1 = time.time()
>        res = self.func(*args, **keyArgs)
>        duration = time.time() - t1
>        args[0].timings.set(self.func.__name__, duration)
>        return res
>
> In the first case the wrapper is called as an unbound function, so it
> works. But in the second case the wrapper is called as a bound method --
> thus args[0] is not func's class instance, and I can't get to the
> timings attribute.

I prefer the function version myself, but to make this work you could
add something like this (untested) to make your wrapper class a
descriptor that tacks on the self argument:

    def __get__(self, instance, owner):
        from functools import partial
        if instance is None:
            return self
        return partial(self, instance)

HTH,
Ian



More information about the Python-list mailing list