Skip to content
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