paint-brush
Python @classmethod vs @staticmethod vs Module-level Functionsby@romblin
550 reads
550 reads

Python @classmethod vs @staticmethod vs Module-level Functions

by Roman BlinovMay 2nd, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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.
featured image - Python @classmethod vs @staticmethod vs Module-level Functions
Roman Blinov HackerNoon profile picture

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.