The difference between @staticmethod and @classmethod in Python
The mechanical difference is one line: a classmethod receives the class, a
staticmethod receives nothing. Why that matters only becomes visible when
somebody subclasses your class, which is where this article spends its time.
What each one receives
class Demo:
def instance_method(self, x): ...
@classmethod
def class_method(cls, x): ...
@staticmethod
def static_method(x): ...
instance_method got self=Demo, x=1
class_method got cls=Demo, x=1
static_method got nothing but x=1
classmethod and staticmethod can both be called on the class directly. An
instance method cannot:
Demo.instance_method(2) -> TypeError: Demo.instance_method() missing 1 required positional argument: 'x'
Because self was never supplied — Demo.instance_method is just a function,
and you would have to pass an instance yourself.
The whole argument, in two lines
Here is why the choice matters. Two factory methods on a base class, one of each kind:
class Base:
@classmethod
def make_cls(cls):
return cls()
@staticmethod
def make_static():
return Base()
class Child(Base):
pass
Child.make_cls() -> Child
Child.make_static() -> Base <- hardcoded Base
cls is whatever class the call came through, so the classmethod builds a
Child. The staticmethod names Base in its body and therefore always
returns a Base, no matter who calls it.
That is the entire case for classmethod, and it is why alternative
constructors must be classmethods:
class Point:
@classmethod
def from_string(cls, text):
x, y = (int(p) for p in text.split(","))
return cls(x, y)
Point.from_string('1,2') : Point(1, 2)
Point3D.from_string('3,4') : Point3D(3, 4) <- correct subclass
Write return Point(x, y) instead of return cls(x, y) and every subclass
silently gets the wrong type back. It is the same mistake as new self()
versus new static() in PHP, if you have met that one.
cls also picks up overridden attributes
Not just the type — anything the subclass changed:
class Animal:
sound = "..."
@classmethod
def describe(cls):
return f"{cls.__name__} says {cls.sound}"
Dog.describe() : Dog says woof
Dog.describe_static() : Animal says ... <- still the base class
So a classmethod is the right choice whenever the behaviour should follow
class-level configuration that subclasses are expected to override.
When a staticmethod is the right answer
When the function genuinely needs neither. It is a helper that belongs to the class for readability and namespacing, and nothing more:
class Temperature:
@staticmethod
def c_to_f(celsius):
return celsius * 9 / 5 + 32
No self, no cls, no class attributes. If you find yourself wanting either,
you wanted a classmethod.
The honest alternative is a module-level function. A staticmethod buys you
grouping and the ability to override it in a subclass; if neither matters, a
plain function is simpler.
What these things actually are
type(Demo.static_method) : function
type(Demo.class_method) : method
type(Demo.instance_method) : function
type(d.instance_method) : method <- bound
A staticmethod is a plain function that happens to live in a class namespace
— which is exactly what it looks like above. A classmethod is already bound
to the class, so it shows up as a method even when reached through the class.
And an instance method is a function on the class but a bound method on an
instance, which is the descriptor protocol doing its work.
Since Python 3.10, staticmethod objects are directly callable, so you can
call one from inside the class body during definition — which used to be a
small annoyance.
The mistake that survives
Forget the decorator and the failure is delayed:
class Oops:
def helper(x):
return x * 2
Oops.helper(5) : 10 <- works when called on the CLASS
Oops().helper(5) -> TypeError: Oops.helper() takes 1 positional argument but 2 were given
Called on the class it works, because nothing is bound and 5 lands in x.
Called on an instance, the instance is passed as the first argument, x
becomes the object, and your real argument has nowhere to go.
The error message is confusing the first time — you passed one argument and it
says you passed two. The invisible one is self.
One practical note
@staticmethod and @classmethod go outermost, on top of any other
decorators:
class Ordered:
@staticmethod
def right():
return "ok"
They wrap the function in a descriptor, so anything applied above them would be decorating the descriptor rather than the function — which almost never does what you want.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).