Python @classmethod vs @staticmethod vs Module-level Functions

Written by romblin | Published 2023/05/02
Tech Story Tags: python | learn-python | programming | code-quality | learn-to-code | learn-code | python-programming | tutorial

TLDRWhen studying Python, you inevitably face the problem of choosing between static methods (@staticmethod), class methods (@classmethod), and module-level functions. In this article, I would like to deal with this issue.via the TL;DR App

When studying Python, you inevitably face the problem of choosing between static methods (@staticmethod), class methods (@classmethod), and module-level functions. In this article, I would like to deal with this issue.

From a technical point of view, the only difference between static and class methods is that the class method gets the class as the first argument.

Consider a simple example:

class Number:

    def __init__(self, value):
        self.value = value

    @classmethod
    def multiply(cls, x, y):
        return cls(x*y)

    @staticmethod
    def divide(x, y):
        return Number(x // y)

In this example, the Number class has two methods: a class method multiply and a static one divide. And we can successfully call both of these methods and it will all work.

>>> n = Number.multiply(1, 2)
>>> n.print()
2
>>> type(n)
<class '__main__.Number'>

>>> n = Number.divide(2, 1)
>>> n.print()
2

>>> type(n)
<class '__main__.Number'>

But what will happen if an inheritance is used?

For example:

class Real(Number):
    pass

The class Real will inherit all the methods of Number, but when we access the divide method, we will not get exactly what we might expect.

>>> r = Real.multiply(1, 2)
>>> r.print()
2
>>> type(r)
<class '__main__.Real'>

>>> r = Real.divide(2, 1)
>>> r.print()
2
>>>type(r)
<class '__main__.Number'>

As we can see, the divide method will return a Number instance instead of Real. To solve this problem, @classmethod-s are just right.

class Number:

    def __init__(self, value):
        self.value = value

    @classmethod
    def divide(cls, x, y):
        return cls(x // y)

    def print(self):
        print(self.value)


class Real(Number): pass

>>> r = Real.divide(2, 1)
>>> r.print()
2

>>>type(r)
<class '__main__.Real'>

So when is it better to use a class method, when a static method, and when a module-level function?

I believe that if a function needs access to a class, then it is a class method, if it does not need access to either the class or an instance of the class, but is logically related to the class, then it is a static method, otherwise, it is a module-level function.


Written by romblin | Python Developer
Published by HackerNoon on 2023/05/02