Python classmethod() | @classmethod Decorator

classmethod() is one of the built-in functions of Python that is bound to a class rather than its object. It simply transforms a method into a class method.

In Python, a class method can be called directly by class name as C.f() or by the class instance C().f()

Note: in order to transform any methods of a class into class methods we have to pass at least one parameter other than self. For the understandable purpose that method is the class method, we pass cls as the first parameter which is treated as a class.

 

Example 1:

Take Dog class in which we transform one of the methods into the class method as:

class Dog:
    age = 3

    def printAge(cls):
        print(f'The age of Dog is {cls.age}')

Dog.cm = classmethod(Dog.printAge)
Dog.cm()

Output:
> The age of Dog is 3

In the above example, we transform the printAge() method into class method by simply passing the Dog class printAge function (Dog.printAge) as a argument in the classmethod() function.

As you noticed, we assigned the converted method into the new function name cm() inside the class. You can give any name as per your wish. For now, simply understood that cm stands for class method.

 

Example 2:

In example 1, the code looks a bit confusing and it is not the Python best practices. In order to make code clear, python’s newer versions support class method decorator or @classmethod that makes the code concise and looks pretty.

@classmethod is one of the Python built-in decorators along with static method and property method decorators. We will cover these decorators in the next blog posts.

class Dog:
    age = 3

    @classmethod
    def printAge(cls, nickname):
        print(f'The age of {nickname} Dog is {cls.age}')

# call the printAge() directly without instance
Dog.printAge("Ciru")

# with instance
Dog().printAge("Jina")

Output:
> The age of Ciru Dog is 3
> The age of Jina Dog is 3

By using the @classmethod decorator, we don’t need to define class methods into classmethod() function separately and it reduces the line of code too.

In printAge() function, we pass two parameters: one for dealing with attributes of class and another for receiving Dog nickname but at the time of calling the function we provided one argument only as equivalent to calling normal class instance methods with self parameter.

 

When to use @classmethod?

For changing class attributes

Suppose let’s us change the Dog class age attributes to the age 7, we can simply do Dog.age = 7

But this process is straightforward, we don’t want any end-users to directly change the attribute value as above also most of the end-users were don’t know whether this attribute exists or not.

By using the instance of the Dog Class, we can’t the change attribute value in all other instances that will be going to be created as they are independent of each other.

In order to resolve this problem, let’s create one method called change_age() decorating with @classmethod as:

class Dog:
    age = 3

    @classmethod
    def change_age(cls, new_age):
        cls.age = new_age
        print(f'The new age of Dog is {cls.age}')

# create d1 Dog instance and change age value
d1 = Dog()
d1.age = 8
print('The age in d1 instance is ->', d1.age)

# create d2 Dog instance and change age value
d2 = Dog()
print('The age in d2 instance is ->', d2.age)

# call the change_age classmethod
Dog.change_age(7)

# create another instance d3 to check new change age
d3 = Dog()
print('The age in d3 instance is ->', d3.age)

Output:
> The age in d1 instance is -> 8
> The age in d2 instance is -> 3
> The new age of Dog is 7
> The age in d3 instance is -> 7

 

Alternatives for constructors

As in the normal class constructors, we have to pass the arguments serially as the __init__(self, ) dunder methods defined. But with the help of @classmethod decorator, we can parse the user inputs accordingly and initialize those parsed values respectively.

class Employee:
    """
    The Employee class that takes fullname, salary, department
    """

    def __init__(self, fullname, salary, department):
        self.fullname = fullname
        self.salary = salary
        self.department = department

    @classmethod
    def take_employee(cls, data):
        """class method to take inputs as name>salary>department
        and parsed them accordinly"""
        user_info = data.split('>')
        return cls(*user_info)

    def display_info(self):
        """function to display employee information"""
        print(f"Fullname: {self.fullname} | Salary: {self.salary} | Department: {self.department}")


# Employee instance
emp1 = Employee("John Doe", "$100k", "IT")
emp1.display_info()

emp2 = Employee.take_employee("Elon Musk>$100000k>CEO")
emp2.display_info()

Output:
> Fullname: John Doe | Salary: $100k | Department: IT
> Fullname: Elon Musk | Salary: $100000k | Department: CEO

 

Leave a Reply