Solution Review: Time My Functions
The solution to the 'Time My Functions' challenge.
We'll cover the following...
import time# Decorator for timing the passed functionsdef 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 methodsdef 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 timingfunctionality 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 thiswill 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.
-
Create a metaclass
TimeMetafeeding class object to the class decorator method -
Create a class decorator method
timeitthat makes use oftime_takendecorator to debug class methods. -
Create
time_takendecorator 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.