...

/

Solution Review: Time My Functions

Solution Review: Time My Functions

The solution to the 'Time My Functions' challenge.

Press + to interact
Python 3.10.4
import time
# Decorator for timing the passed functions
def time_taken(func):
def wrapper(*args, **kwargs):
start = time.time()
resp = func(*args, **kwargs)
end = time.time()
return (end - start)
return wrapper
# Class decorator making use of time_taken decorator to debug class methods
def timeit(cls):
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, time_taken(val))
return cls
''' Metaclass feeding created class object to timeit method to get timing
functionality enabled objects '''
class TimeMeta(type):
def __new__(cls, clsname, bases, clsdict):
obj = super().__new__(cls, clsname, bases, clsdict)
obj = timeit(obj)
return obj
''' Base class with metaclass TimeMeta, now all the subclass of this
will have timing applied '''
class Animal(metaclass=TimeMeta):
def talk(self):
time.sleep(1)
print("Animal talk")
class Cow(Animal):
def talk(self):
time.sleep(1)
print("Moo")
class Dog(Animal):
def talk(self):
time.sleep(1)
print("Bark")
animal = Animal()
cow = Cow()
dog = Dog()
print(animal.talk())
print(cow.talk())
print(dog.talk())

Explanation

The solution consists three main steps.

  1. Create a metaclass TimeMeta feeding class object to the class decorator method

  2. Create a class decorator method timeit that makes use of time_taken decorator to debug class methods.

  3. Create time_taken decorator that times the passed method by keeping track of start time and end time.

Let’s discuss them one by one.

Creating a metaclass

Look at line 26. We create a class TimeMeta deriving from type. We define the __new__ method for this class. Here, we are calling __new__ of type class (using the super keyword) to create a new class obj. At line 29, we are applying the decorator to this class with the timeit function before returning it.

Creating a class decorator

Look at line 18. We call vars() on the class cls. It will return a dictionary containing all the fields/elements of cls. For each value in the dictionary, we check whether it’s callable or not. If it’s callable, it means that it is a method, and it needs to be timed. We timed the method by calling time_taken on the value and update the value of key with the setattr function. Now we have the decorated class cls, so we return it.

Decorator for timing passed functions

Look at line 4. We calculate the start time (line 8) before starting the passed function. Likewise, we calculate the end time (line 10) after the passed function is terminated. We import the time package for this functionality (see line 1). We subtract the start time from the end time (end - start).

Now, these above three steps make sure that whenever a method from a class of type TimeMeta or from a class inheriting the base class of type TimeMeta is called, it will be timed.

Look at line 35. We make a class Animal with a function talk. We further make two classes inheriting the Animal class: Cow and Dog. These two classes also have the talk method printing the noise they make.

When we call talk on the object of Animal, Cow, and Dog class, they all display the timings after displaying the noises they make.


Ask