Three method types, one class
Python classes support three kinds of methods, and the distinction trips up almost every developer at some point. The difference is not about access control — it's about what the method receives as its implicit first argument, which determines what it can see and do.
Quick-reference comparison
| Instance method | @classmethod | @staticmethod | |
|---|---|---|---|
| First argument | self — the instance | cls — the class itself | Nothing — no implicit arg |
| Access to instance? | Yes | No | No |
| Access to class? | Yes (via type(self) or self.__class__) | Yes (via cls) | No |
| Callable on class? | No (needs an instance) | Yes | Yes |
| Callable on instance? | Yes | Yes (cls is still the class) | Yes |
| Subclass safe? | Yes — type(self) gives subclass | Yes — cls gives the subclass | No — no class reference at all |
| Main use | Per-object behaviour | Alternative constructors, class-level logic | Utility helpers with no class/instance coupling |
Instance methods — the default
An instance method takes self as its first argument and operates on one particular
object. It has full access to instance state and can also reach the class via type(self)
or self.__class__.
class BankAccount:
interest_rate = 0.05 # class-level state
def __init__(self, balance):
self.balance = balance # instance-level state
def deposit(self, amount): # instance method — needs self
self.balance += amount
return self.balance
account = BankAccount(1000)
account.deposit(200) # self = account, amount = 200
Instance methods are the right choice for anything that operates on or mutates a specific object's state.
@classmethod — the alternative constructor pattern
A classmethod receives cls (the class, not an instance) as its first argument. It
can't see instance state, but it can create instances, read or modify class-level
attributes, and call other class methods. The most common use is an alternative
constructor — a named factory method for building instances from a different input
format.
class Date:
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_string(cls, text): # alternative constructor
year, month, day = map(int, text.split("-"))
return cls(year, month, day) # cls, not Date — subclass safe
@classmethod
def today(cls):
import datetime
t = datetime.date.today()
return cls(t.year, t.month, t.day)
Date.from_string("2026-06-21") # no instance needed
Date.today()
Why cls instead of Date? Subclasses inherit the classmethod. Using cls means a
subclass gets an instance of itself — using the hard-coded class name Date breaks
subclassing:
class ExtendedDate(Date):
pass
type(ExtendedDate.from_string("2026-01-01")) # ExtendedDate — correct with cls
# would be Date — wrong if from_string() used `Date(...)` directly
This is the same pattern as dict.fromkeys, datetime.datetime.fromtimestamp, and
int.from_bytes in the standard library.
Deep dive: Methods & Properties interview questions
@staticmethod — a regular function inside the class namespace
A staticmethod receives no implicit first argument — it's a plain function that lives inside the class for organisational reasons only. It has no access to instance or class state unless you pass something in explicitly.
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def to_fahrenheit(self): # instance method
return self.celsius * 9/5 + 32
@staticmethod
def celsius_to_fahrenheit(c): # no self, no cls
return c * 9/5 + 32 # pure conversion helper
Temperature.celsius_to_fahrenheit(100) # 212.0 — no instance needed
Temperature(0).celsius_to_fahrenheit(100) # same result — self is not passed
Use a staticmethod when the logic is related to the class conceptually (so you want it in the same namespace) but doesn't actually need to touch the class or any instance.
The subclass safety rule
The biggest practical difference between @classmethod and @staticmethod is subclass
behaviour. A classmethod knows which (sub)class it was called on; a staticmethod doesn't:
class Shape:
@classmethod
def default(cls):
return cls() # builds the right subtype
@staticmethod
def default_bad():
return Shape() # always Shape, never the subclass
class Circle(Shape):
pass
type(Circle.default()) # Circle — correct
type(Circle.default_bad()) # Shape — wrong
Rule of thumb: any factory or constructor method that subclasses might inherit should
be a @classmethod, never a @staticmethod.
The decision rule
Answer two questions:
- Does the method need access to
self(instance state)? → Instance method. - Does the method need to create instances of the correct (sub)class or access
class-level state, but not a specific instance?
→
@classmethod. - Is the method a utility helper that doesn't touch
selforclsat all? →@staticmethod.
class User:
role = "user" # class attribute
def __init__(self, name, email):
self.name = name # instance attribute
self.email = email
def greet(self): # instance method — uses self
return f"Hi, I'm {self.name}"
@classmethod
def from_dict(cls, data): # classmethod — factory from dict
return cls(data["name"], data["email"])
@staticmethod
def is_valid_email(email): # staticmethod — pure utility
return "@" in email and "." in email
Recap
An instance method takes self and is the right default for any per-object behaviour.
A @classmethod takes cls — use it for alternative constructors and class-level
logic, always with cls(...) rather than the hard-coded class name so subclasses get the
right type. A @staticmethod takes nothing implicit — use it for utility helpers that
belong in the class namespace but genuinely don't interact with any instance or class
state. When in doubt between the two non-instance options, prefer @classmethod if there's
any chance a subclass will call it.