Back to Course

Intermediate Python

0% Complete
0/0 Steps
Lesson 20 of 33
In Progress

@classmethod and @staticmethod Decorators

In Python, a classmethod is a method that is bound to the class and not the instance of the class. A classmethod is defined using the @classmethod decorator, and it takes the class itself as the first argument (cls). A classmethod can be called on the class itself, or on any instance of the class.

On the other hand, a staticmethod is a method that is neither bound to the class nor the instance of the class. A staticmethod is defined using the @staticmethod decorator, and it does not take any special arguments. A staticmethod can be called on the class itself, or on any instance of the class.

In this article, we will explore the use cases for classmethods and staticmethods, and how to define and use them in Python.

Defining Classmethods:

To define a classmethod, we use the @classmethod decorator followed by the cls argument, like this:

class MyClass:
    def __init__(self, arg):
        self.arg = arg

    @classmethod
    def my_classmethod(cls, arg):
        return cls(arg)

In the example above, we have defined a class MyClass with an instance variable arg and a classmethod my_classmethod. The classmethod takes a single argument arg, and it returns an instance of MyClass with the value of arg as the arg instance variable.

We can call the classmethod on the class itself, or on any instance of the class, like this:

# call the classmethod on the class itself
obj1 = MyClass.my_classmethod('hello')
print(obj1.arg)  # Output: 'hello'

# call the classmethod on an instance of the class
obj2 = MyClass('world')
obj3 = obj2.my_classmethod('hello')
print(obj3.arg)  # Output: 'hello'

Use Cases for Classmethods:

Classmethods are useful in cases where we want to create an instance of a class based on a specific input or configuration. For example, we can use a classmethod to create an instance of a class with a certain attribute value, or with a value that is calculated based on the input.

Here’s an example of using a classmethod to create an instance of a class with a certain attribute value:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def from_string(cls, string):
        name, age = string.split(',')
        return cls(name, age)

# create a Person object using the from_string classmethod
p = Person.from_string('John, 30')
print(p.name)  # Output: 'John'
print(p.age)  # Output: '30'

In the example above, we have defined a class Person with an instance variable name and an instance variable age. We have also defined a classmethod from_string, which takes a string as an input and creates an instance of Person with the name and age values extracted from the string.

We can use the from_string classmethod to create an instance of Person from a string, rather than calling the constructor Person(name, age) directly. This can be useful in cases where we want to create an instance of a class based on a string input, such as reading data from a file.

Defining Staticmethods:

To define a staticmethod, we use the @staticmethod decorator, like this:

class MyClass:
    def __init__(self, arg):
        self.arg = arg

    @staticmethod
    def my_staticmethod(arg):
        return arg

In the example above, we have defined a class MyClass with an instance variable arg and a staticmethod my_staticmethod. The staticmethod takes a single argument arg, and it returns the value of arg.

We can call the staticmethod on the class itself, or on any instance of the class, like this:

# call the staticmethod on the class itself
result1 = MyClass.my_staticmethod('hello')
print(result1)  # Output: 'hello'

# call the staticmethod on an instance of the class
obj = MyClass('world')
result2 = obj.my_staticmethod('hello')
print(result2)  # Output: 'hello'

Use Cases for Staticmethods:

Staticmethods are useful in cases where we want to define a method that does not depend on the instance or the class itself, but it is related to the class in some way. A staticmethod is just a function that is defined within a class, but it does not have any special behavior.

Here’s an example of using a staticmethod to define a utility function that is related to the class:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @staticmethod
    def is_adult(age):
        return age >= 18

# call the is_adult staticmethod
result = Person.is_adult(20)
print(result)  # Output: True

In the example above, we have defined a class Person with an instance variable name and an instance variable age. We have also defined a staticmethod is_adult, which takes an age as an input and returns a boolean value indicating whether the person is an adult or not.

The is_adult staticmethod does not depend on the instance or the class itself, it is just a utility function that is related to the class Person. We can call the staticmethod on the class itself, or on any instance of the class, to check if a person with a certain age is an adult.

Conclusion:

In this article, we have learned about classmethods and staticmethods in Python, and how to define and use them. Classmethods are useful in cases where we want to create an instance of a class based on a specific input or configuration. Staticmethods are useful in cases where we want to define a method that does not depend on the instance or the class itself, but it is related to the class in some way. We hope this article has been helpful and that you have a better understanding of classmethods and staticmethods in Python.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Write a class Rectangle that has a width property and a height property, and define a classmethod from_square that creates an instance of Rectangle with the given width and height set to the same value. Test your class by creating a Rectangle object using the from_square classmethod, and print the width and the height of the object.

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @classmethod
    def from_square(cls, side):
        return cls(side, side)

# create a Rectangle object using the from_square classmethod
r = Rectangle.from_square(5)
print(r.width)  # Output: 5
print(r.height)  # Output: 5

Write a class Point that has an x property and a y property, and define a staticmethod distance that calculates the distance between two points. Test your class by creating two Point objects, and use the distance staticmethod to calculate the distance between them.

import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @staticmethod
    def distance(p1, p2):
        dx = p1.x - p2.x
        dy = p1.y - p2.y
        return math.sqrt(dx**2 + dy**2)

# create two Point objects
p1 = Point(1, 2)
p2 = Point(4, 5)

# calculate the distance between p1 and p2
result = Point.distance(p1, p2)
print(result)  # Output: 3.605551275463989

Write a class Polygon that has a sides property and a perimeter property, and define a classmethod from_side_lengths that creates an instance of Polygon with the given side lengths. Test your class by creating a Polygon object using the from_side_lengths classmethod, and print the sides and the perimeter of the object.

class Polygon:
    def __init__(self, sides, perimeter):
        self.sides = sides
        self.perimeter = perimeter

    @classmethod
    def from_side_lengths(cls, *side_lengths):
        return cls(side_lengths, sum(side_lengths))

# create a Polygon object using the from_side_lengths classmethod
p = Polygon.from_side_lengths(5, 7, 9)
print(p.sides)  # Output: (5, 7, 9)
print(p.perimeter)  # Output: 21

Write a class Shape that has a color property, and define a staticmethod parse_color that parses a color string into a tuple of RGB values. Test your class by creating a Shape object with a color string and use the parse_color staticmethod to parse it into RGB values.

class Shape:
    def __init__(self, color):
        self.color = color

    @staticmethod
    def parse_color(color_string):
        # split the color string into separate R, G, B values
        r, g, b = color_string.split(",")
        # return the values as a tuple
        return (int(r), int(g), int(b))

# create a Shape object with a color string
s = Shape("255,128,0")
# parse the color string into RGB values
rgb = Shape.parse_color(s.color)
print(rgb)  # Output: (255, 128, 0)

Write a class Card that has a suit property and a value property, and define a classmethod from_string that creates an instance of Card with the given suit and value extracted from a string. Test your class by creating a Card object using the from_string classmethod, and print the suit and value of the object.

class Card:
    def __init__(self, suit, value):
        self.suit = suit
        self.value = value

    @classmethod
    def from_string(cls, card_string):
        # split the card string into suit and value
        suit, value = card_string.split("-")
        return cls(suit, value)

# create a Card object using the from_string classmethod
c = Card.from_string("hearts-7")
print(c.suit)  # Output: hearts
print(c.value)  # Output: 7