Blog

classmethod vs staticmethod in Python

Three kinds of method, and the choice between them is not a style question. It is a question of what the method actually needs.

  • An instance method needs this particular object.
  • A classmethod needs the class.
  • A staticmethod needs neither.

All three in one class

from datetime import date

class Employee:
    raise_pct = 1.05

    def __init__(self, name, salary):
        self.name, self.salary = name, salary

    def give_raise(self):                      # instance method: needs this employee
        self.salary = round(self.salary * self.raise_pct, 2)

    @classmethod
    def from_csv(cls, line):                   # classmethod: needs the class
        name, salary = line.split(',')
        return cls(name, float(salary))

    @staticmethod
    def is_payday(d):                          # staticmethod: needs neither
        return d.day == 25

e = Employee.from_csv('Ada,50000')
e.give_raise()
print(e.name, e.salary)
print(Employee.is_payday(date(2026, 9, 25)), Employee.is_payday(date(2026, 9, 3)))

It prints:

Ada 52500.0
True False

from_csv is the classic classmethod: an alternative constructor. Python has one __init__ per class, so every other way of building the object becomes a classmethod.

Why from_csv uses cls

This is the part that matters, and the reason a classmethod is not just a staticmethod that happens to receive the class.

# why from_csv uses cls and not Employee
class Contractor(Employee):
    raise_pct = 1.10

c = Contractor.from_csv('Grace,60000')
print(type(c).__name__, c.name, c.salary)
c.give_raise()
print('after raise:', c.salary)

It prints:

Contractor Grace 60000.0
after raise: 66000.0

Contractor.from_csv produced a Contractor, not an Employee, and the raise used 1.10. Nobody wrote a from_csv on Contractorcls was Contractor because that is what the call was made on.

Hard-code the class and that stops working:

# hard-coding the class breaks the subclass
class Broken(Employee):
    @classmethod
    def from_csv(cls, line):
        name, salary = line.split(',')
        return Employee(name, float(salary))   # wrong: always the base class

print(type(Broken.from_csv('Alan,70000')).__name__)

It prints:

Employee

Broken.from_csv returns an Employee. Every subclass silently gets the wrong type, and nothing raises. Alternative constructors use cls, always.

What each one receives

class Show:
    def instance(self):  return f"got {type(self).__name__} instance"
    @classmethod
    def klass(cls):      return f"got the class {cls.__name__}"
    @staticmethod
    def static():        return "got nothing"

s = Show()
print(s.instance())
print(Show.klass())
print(Show.static())

It prints:

got Show instance
got the class Show
got nothing

When to use a staticmethod

Rarely, and the honest answer is: when a plain function would do but you want it namespaced with the class it relates to. is_payday does not touch an employee or the class — it is a function about dates.

If a staticmethod has nothing to do with the class either, it wants to be a module-level function. Putting it in a class buys you nothing but a longer name.

What to remember

  • Instance method for anything that reads or changes this object.

  • Classmethod for alternative constructors, and use cls so subclasses work.

  • Staticmethod when the method needs neither, and only when grouping it with the class genuinely helps.

  • Hard-coding the class name inside an alternative constructor is a bug that only shows up once someone subclasses you.

How useful was this post?

Click on a heart to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.