Home Tutorials OOP

OOP

Static Methods vs Class Methods vs Instance Methods in Python

Pyford Notes July 6, 2026 6 min read
Key points
  • A plain method automatically receives the instance as its first argument, conventionally named self.
  • A @classmethod receives the class itself, conventionally named cls, so it works correctly even when called on a subclass.
  • A @staticmethod receives neither self nor cls; it's a regular function that happens to live inside the class's namespace.
  • @classmethod is the standard way to write alternative constructors, because cls resolves to the subclass when called from one.

Instance methods: the default, bound to self

Any method defined without a decorator inside a class is an instance method. When you call it on an object, Python automatically passes that object as the first argument, which by convention is named self:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

r = Rectangle(3, 4)
print(r.area())   # 12, equivalent to Rectangle.area(r)

The dot-call syntax r.area() is shorthand for Rectangle.area(r) — the instance is inserted as the first argument behind the scenes. This is the right default whenever a method needs to read or change something about a particular object's state.

Class methods: bound to the class, not the instance

A method decorated with @classmethod receives the class itself as its first argument, conventionally named cls, instead of an instance:

class Rectangle:
    count = 0

    def __init__(self, width, height):
        self.width = width
        self.height = height
        Rectangle.count += 1

    @classmethod
    def instances_created(cls):
        return cls.count

print(Rectangle.instances_created())   # can be called on the class directly

Because cls is the class the method was actually called through — not hardcoded to the class where it was defined — a class method called on a subclass receives that subclass, not the parent. That distinction matters a lot for the most common use of class methods: alternative constructors, covered below.

Static methods: no automatic argument at all

A method decorated with @staticmethod gets no automatic first argument. It behaves exactly like a plain function, just namespaced under the class for organisation:

class Temperature:
    @staticmethod
    def celsius_to_fahrenheit(c):
        return c * 9 / 5 + 32

print(Temperature.celsius_to_fahrenheit(100))   # 212

A static method is appropriate when the logic is conceptually related to the class but doesn't need access to either the instance or the class itself — a conversion helper, a validation check, a small utility that just happens to belong next to the class that uses it. If a method's body never touches self or the class, that's usually the sign it should be static rather than an instance method.

The classic use case: alternative constructors

__init__ only gets one calling convention. When a class needs to be built from several different kinds of input — a string, a dictionary, another file format — class methods provide named alternative constructors that all funnel into the same class:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @classmethod
    def from_square(cls, side):
        return cls(side, side)

    @classmethod
    def from_dict(cls, data):
        return cls(data["width"], data["height"])

sq = Rectangle.from_square(5)
r2 = Rectangle.from_dict({"width": 3, "height": 7})

Using cls(...) rather than Rectangle(...) is what makes this work correctly for subclasses too — Square.from_square(5) would build a Square, not a Rectangle, because cls resolves to whichever class the method was actually called on.

How each behaves under inheritance

The three respond differently when a subclass overrides part of the picture. An instance method resolves through the normal method resolution order, so overriding it in a subclass replaces the behaviour for that subclass's instances, same as always. A class method's cls argument automatically becomes the subclass when called through it, which is exactly what makes the alternative-constructor pattern work without any extra code in the subclass. A static method carries no binding information at all — it can be overridden like any other attribute, but it never receives information about which class or instance it was accessed through, so it can't adapt its behaviour based on that context even if a subclass wanted it to.

Choosing between the three

The decision usually falls out of what the method actually needs to touch. If it reads or modifies instance state, it's an instance method. If it needs the class but not a particular instance — building an object a different way, tracking or reporting class-level state — it's a class method. If it needs neither, and is just grouped with the class for readability, it's a static method. Reaching for @staticmethod out of habit on something that actually needs cls is a common early mistake; the giveaway is a static method that has to hardcode the class name somewhere inside its body instead of using cls, which quietly breaks the moment the class is subclassed. The precise mechanics of both decorators are documented under the built-in functions reference.