Class methods allow us to define alternative initializers (also known as factory methods) in a class. These methods help us create instance objects from different types of input data. Let us understand this with the help of an example. Again, we take the same Person class. We have deleted the class variables to keep it short and simple.
class Person: def __init__(self, name, age): self.name = name self.age = age def display(self): print(f'{self.name} is {self.age} years old') p1 = Person('Devanshi', 18) p2 = Person('Devank', 10)
We can initialize a new instance object of this Person class in only one way, by providing values of name and age. There may be situations when we want to create instance objects of type Person from different types of data. For example, we may have a string that contains name and age separated by a comma, or we may have a dictionary that contains name and age.
s = 'Jack, 23'
d = {'name': 'Jane', 'age': 34}
You might read this type of data from a file or from any other place. Now you want to be able to create an instance of type Person from these types of strings and dictionaries.
Python does not support function overloading, so there can be only one type of initializer. We cannot have more than one definition for __init__ method inside a class. To initialize our objects in different ways, we can use class methods. In our Person class we will add two class methods named from_str and from_dict.
class Person: def __init__(self, name, age): self.name = name self.age = age @classmethod def from_str(cls, s): name, age = s.split(',') return cls(name, int(age)) @classmethod def from_dict(cls, d): return cls(d['name'], d['age']) def display(self): print(f'{self.name} is {self.age} years old') s = 'Jack, 23' d = {'name': 'Jane', 'age': 34} p3 = Person.from_str(s) p3.display() p4 = Person.from_dict(d) p4.display()
Output-
Jack is 23 years old
Jane is 34 years old
The method from_str takes a string as argument and creates and returns a Person object, and the method from_dict creates a Person object from a dictionary.
In the from_str method, cls as usual is the first parameter and the next parameter s is for accepting a string. We split the string s to get the values of name and age. After that we create a new instance object of type Person by using these values of name and age. We know that inside the class methods, the cls parameter refers to the class object. So, cls here refers to the Person class and writing cls(name, int(age)) is equivalent to writing Person(name, int(age)) and it will create a new Person instance object. It will call __init__ to initialize the newly created object.
Similarly, our class method from_dict creates and returns a Person object from a dictionary with 'name' and 'age' as keys. We have sent the values of the dictionary as argument to the Person class initializer.
The methods from_str and from_dict are called with the class name. The instance objects that are returned by these methods are assigned to names p3 and p4.
We can see that both the factory methods internally use the __init__ method to create and return the instance objects. Instead of hardcoding the class name in these methods we have used the cls parameter to create the objects. This is good, if in future we have to rename the class or inherit a new class from this class. These factory methods would work for any class inherited from the Person class.
So, if we want to create factory methods that support inheritance, we should use class methods.
Instead of using these class methods, you might be tempted to change your __init__ so that it works with different types of input data. You might think of using default arguments or variable number of arguments and then use checks inside the method to process data differently in each case. This approach can sometimes work, but it makes the code difficult to understand and maintain. The if..elif..else construct could be confusing if there are many cases to consider. The class methods approach is simpler to understand and also increases the readability of the calling code. The __init__ method is generally a simple method that initializes the instance variables from the arguments. The alternative initializers can do additional pre-processing of data to create the instance. Rather than cluttering our __init__ method with all the code, we create separate initializers.