Question

So let's say i have the following code where I am using the get_duration decorator to print out how long a method lasted.

import datetime
import time

def get_duration(function):
  def wrapper(*args, **kwargs):
    start = datetime.datetime.now()
    print "starting process %s()" %function.__name__
    function(*args, **kwargs)
    end = datetime.datetime.now()
    print "Process lasted: ", end - start
  return wrapper

class Operations(object):

  def __init__(self, a, b):
    self.a = a
    self.b = b

  @get_duration
  def add(self):
    time.sleep(1)
    print self.a + self.b

  @get_duration
  def sub(self):
    time.sleep(1)
    print self.a - self.b


my_class = Operations(2, 98)
my_class.add()
my_class.sub()

Output

>> starting process add()
>> 100
>> Process lasted:  0:00:01.000260
>> starting process sub()
>> -96
>> Process lasted:  0:00:01.001161

Can i replace this with a class method or something like (pseudo code following):

my_class = Operations(2, 98)
my_class.add.get_duration #runs add(), prints duration
my_class.add #runs add() does not print duration
Was it helpful?

Solution

To do exactly that you want, you can decorate this method by the callable class.

class TimeElapser  :
   def __init__(self, function_to_wrap) :
       self.__call__ = function_to_wrap
       self.get_duration = get_duration(function_to_wrap)

Examples :

class Foo :

    @TimeElapser
    def do_something(self, ....) :
       ...

foo = Foo()
foo.do_something() #Does something
foo.do_something.get_duration() # Does something and print duration

OTHER TIPS

Something like this?

def map_func(function):
    def wrapper(*args, **kwargs):
        start = datetime.datetime.now()
        print "starting process %s()" %function.__name__
        function(*args, **kwargs)
        end = datetime.datetime.now()
        print "Process lasted: ", end - start
    function.get_duration = wrapper
    return function

Demo:

>>> c = Operations(100, 200)
>>> c.add()
c.300
>>> c.sub()
-100
>>> c.add.get_duration(c)
starting process add()
300
Process lasted:  0:00:01.001584
>>> c.sub.get_duration(c)
starting process sub()
-100
Process lasted:  0:00:01.003768

You can do this:

def get_duration(function):
  def wrapper(*args, **kwargs):
    kwargs['self'].elapsed_time = timeit.timeit('function(*args, **kwargs)',
                                      "from __main__ import function, args, kwargs",
                                      number=1)
  return wrapper

Then you can read elapsed_time var to get the time.

I think this is a good use case for metaprogramming like so:

import types

def my_dec(f):
    print "decorated"
    return f

class MyMetaprogrammingDecoratorClass(object):
    def __getattr__(self, attribute):
        try:
            ## we should honor inherited classes that may have defined __getattr__
            return super(MyMetaprogrammingDecoratorClass, self).__getattr__(attribute)
        except AttributeError:
            if attribute.endswith("_decorated"):
                output = object.__getattribute__(self, attribute.replace("_decorated",""))
                if isinstance(output, types.MethodType):
                    return my_dec(output)
                else:
                    raise AttributeError
            else:
                raise AttributeError

class MyCustomClass(MyMetaprogrammingDecoratorClass):
    def f(self):
        return 1

g = MyCustomClass()
print g.f()
>> 1
print g.f_decorated()
>> decorated
>> 1

So you can just postfix any method with _decorated and it the class will know to decorate that method with my_dec, if it exists.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top