datarekha
Python Easy Asked at GoogleAsked at AmazonAsked at MetaAsked at Microsoft

What is the difference between an instance method, a class method, and a static method in Python?

The short answer

Instance methods receive the instance as the first argument (`self`) and can read and modify instance state. Class methods receive the class as the first argument (`cls`) via `@classmethod` and are used for alternative constructors or class-level operations. Static methods receive no implicit argument and are plain functions namespaced inside a class.

How to think about it

Strip the question to its core and it asks one thing: what implicit first argument does Python inject, and why? That choice — made before your method body even runs — is the whole distinction.

  • no decorator → instance method → Python passes the calling instance as self
  • @classmethod → Python passes the class as cls
  • @staticmethod → Python passes nothing extra; it’s a plain function that happens to live in the class

A worked example

One Circle class using all three, plus the reason cls matters:

import math

class Circle:
    _unit = "cm"                       # class attribute, shared by all instances

    def __init__(self, radius):
        self.radius = radius           # instance attribute

    # Instance method — needs self (per-instance state)
    def area(self):
        return math.pi * self.radius ** 2

    def describe(self):
        return f"Circle(r={self.radius} {Circle._unit})"

    # Class method — receives the class; the idiomatic "alternative constructor"
    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter / 2)       # cls(...) — not Circle(...) — so subclasses work

    @classmethod
    def set_unit(cls, unit):
        cls._unit = unit               # mutates class-level state

    # Static method — no implicit arg; a utility that belongs here by topic
    @staticmethod
    def validate_radius(r):
        return r > 0

c1 = Circle(5)
print("area     :", round(c1.area(), 2))
print("describe :", c1.describe())

c2 = Circle.from_diameter(10)          # alternative constructor
print("from_dia :", c2.radius)

print("valid(5) :", Circle.validate_radius(5))
print("valid(-1):", Circle.validate_radius(-1))

Circle.set_unit("m")                   # change the shared class attribute
print("after set_unit:", c1.describe())   # every instance sees it
print("               ", c2.describe())
area     : 78.54
describe : Circle(r=5 cm)
from_dia : 5.0
valid(5) : True
valid(-1): False
after set_unit: Circle(r=5 m)
                Circle(r=5.0 m)

set_unit changes one class attribute and both circles report the new unit — class-level state in action. And from_diameter is the workhorse use of @classmethod: a second way to build the object, written once.

First argDecoratorTypical use
Instanceselfnoneread/write instance state
Classcls@classmethodalternative constructors, class-level state
Static@staticmethodutilities that touch neither self nor cls

Why cls beats hardcoding the class name

The payoff hides in from_diameter. Because it calls cls(diameter / 2) instead of Circle(diameter / 2), it returns whatever class invoked it. Subclass Circle as Sphere and Sphere.from_diameter(10) builds a Spherecls is Sphere. Hardcode Circle(...) and the factory would stubbornly return a Circle no matter who called it.

Learn it properly Classes & Instances

Keep practising

All Python questions

Explore further

Skip to content