@classmethod vs. @staticmethod in Python

Jose Salvatierra
School of Code
Published in
3 min readFeb 9, 2016

Something that unavoidably ends up coming up when learning Python is this issue of classmethods and staticmethods, especially for students coming from a Java background, where there are no classmethods.

In Python, we can define these using their respective decorators. Below a not-very-useful example which nevertheless illustrates what they look like:

class Number():
def __init__(self, value):
self.value = value
@classmethod
def sum(cls, value1, value2):
return cls(value1+value2)
def print(self):
print(str(self.value))

The only difference between @classmethod and @staticmethod is that in the @classmethod, the class is bound to the method as the first argument (cls). This means that it is easy to access the class, in the method, via the cls argument, instead of having to use the full class name.

So in the case of our sum(cls, value1, value2) method, the cls argument would be the Number class, which is the class in which the method lives.

But you may say: “it makes sense, but adds another bit of complexity that isn’t necessary, as we could just use the class name?

Lets do that instead, and not use @classmethod, and see what happens…

class Number():
def __init__(self, value):
self.value = value
@staticmethod
def sum(value1, value2):
return Number(value1+value2)
def print(self):
print(str(self.value))

And this works, except when we use inheritance. In inheritance, a sub-class (also known as child class) inherits the methods of the parent class.

If we created a sub-class of Number, it would contain all of the Number methods, and it could also define more methods of its own.

class Float(Number):
# Skip defining an __init__ method, and it uses the same as Number
# Skip defining the sum() method, and it uses the same as Number # Skip defining the print method, and it uses the same as Number
# Or we could define our own print method for this class.
def print(self):
# Prints the number with 2 decimal places
print("{:.2f}".format(self.value))

So now we could call methods like so:

>>> n = Number(0.15647)
>>> n.print()
0.15647
>>> f = Float(0.15647)
>>> f.print()
0.15

However, if we call the Float.sum() method, bad things start to happen…

>>> f = Float.sum(0.11, 0.1593)
>>> f.print() # This should only print 2 decimal places!
0.2693

The Float.sum(value1, value2) method is actually returning a Number instance, and not a Float instance.

So our variable f is actually not a Float, it is a Number. When we call f.print() we are calling the print() method from the Number class, and not the one we overwrote in our Float class.

The solution is to go back to using @classmethod. That way, we use cls instead of Number, and cls always refers to the class that we are calling from. If we use Number.sum(10, 15), we would get back a Number instance. If we use Float.sum(15.474, 19.232), we would get back a Float instance.

So when should you use @staticmethod?

Only use @staticmethod when you want to place a method inside a class that is logically related to the class, but does not necessarily interact with any specific instance.

For example, I would use static methods when defining a database layer. I would define a Database class which has a few static methods to insert or find data from the database. At no point am I creating Database instances that contain that data, but the methods to find and insert are related to a Database.

--

--