
Class Methods vs Static Methods vs Instance Methods in Python
Python classes can have three kinds of methods. Regular instance methods are the ones you write all the time. Then there are methods decorated with @classmethod and @staticmethod, which show up in library code and interview questions and tend to get explained with abstract examples that don't make it clear when you'd actually choose one over another.
The difference comes down to one question: what does the method receive as its first argument? An instance method gets the object, a class method gets the class, and a static method gets nothing extra. Everything else, including when to use each, follows from that.
This post shows how the three behave side by side, then looks at the real use cases: alternative constructors with @classmethod, helpers with @staticmethod, class-level state, and how each behaves under inheritance. If you need a refresher on self and __init__, see classes and objects in Python.
The Three Kinds Side by Side
class Demo:
def instance_method(self) -> str:
return f"instance method called with {self!r}"
@classmethod
def class_method(cls) -> str:
return f"class method called with {cls!r}"
@staticmethod
def static_method() -> str:
return "static method called with nothing"
def __repr__(self) -> str:
return "<Demo instance>"
d = Demo()
print(d.instance_method())
print(d.class_method())
print(d.static_method())
print(Demo.class_method())
print(Demo.static_method())
instance method called with <Demo instance>
class method called with <class '__main__.Demo'>
static method called with nothing
class method called with <class '__main__.Demo'>
static method called with nothing
- Instance method (no decorator): the first parameter,
self, is the instance it was called on. It can read and change that object's data, and reach the class throughtype(self). - Class method (
@classmethod): the first parameter,clsby convention, is the class. It's the same whether you call it on the class (Demo.class_method()) or an instance (d.class_method()). It can't see any particular instance's data, but it can read class attributes and create new instances. - Static method (
@staticmethod): no automatic first argument at all. It's a plain function that happens to live in the class's namespace.
Calling an instance method on the class, without an instance, fails because there's nothing to fill self with:
Demo.instance_method()
TypeError: Demo.instance_method() missing 1 required positional argument: 'self'
You can pass the instance explicitly, Demo.instance_method(d), which shows that d.instance_method() is really just shorthand for that.
What Python Does with Each
When you access a method through an instance, Python binds it, attaching the first argument for you. Printing the methods shows the difference:
print(d.instance_method)
print(Demo.class_method)
print(d.static_method)
<bound method Demo.instance_method of <Demo instance>>
<bound method Demo.class_method of <class '__main__.Demo'>>
<function Demo.static_method at 0x1096bc4a0>
The instance method is bound to d. The class method is bound to the class, even when accessed through an instance. The static method isn't bound to anything; it's handed back as a plain function. The decorators work by changing how this binding happens (they're descriptors, the same mechanism behind @property).
Quick Comparison
| Instance method | Class method | Static method | |
|---|---|---|---|
| Decorator | None | @classmethod | @staticmethod |
| First argument | The instance (self) | The class (cls) | None |
| Read/modify instance state | Yes | No | No |
| Read/modify class state | Yes, via type(self) | Yes | Only by naming the class |
| Callable on the class | Only with an instance passed | Yes | Yes |
| Respects subclasses | Yes | Yes, cls is the subclass | No |
| Typical use | Most behavior | Alternative constructors, class-wide state | Helpers related to the class |
Instance Methods: The Default
Most methods should be instance methods. If a method uses self to read or change the object's data, that's what it is, and there's nothing more to decide. The interesting question is the reverse: when a method doesn't use self, should it be a class method, a static method, or not a method at all?
Class Methods: Alternative Constructors
The most common and most useful reason to write a class method is to provide other ways of creating an object. A class has one __init__, but objects often come from different sources: a string, a dictionary, a file, a database row.
# events.py
import re
from datetime import date
from typing import Any, Self
class Event:
_DATE_PATTERN = re.compile(r"^(\d{4})-(\d{2})-(\d{2})$")
def __init__(self, title: str, day: date) -> None:
self.title = title
self.day = day
def __repr__(self) -> str:
return f"{type(self).__name__}({self.title!r}, {self.day.isoformat()})"
# Instance method: works with one event's data.
def days_until(self, today: date) -> int:
return (self.day - today).days
# Class methods: alternative constructors.
@classmethod
def from_string(cls, text: str) -> Self:
title, _, raw_date = text.rpartition("@")
return cls(title.strip(), cls.parse_date(raw_date.strip()))
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
return cls(data["title"], date.fromisoformat(data["day"]))
# Static method: a helper that needs neither the class nor an instance.
@staticmethod
def parse_date(text: str) -> date:
match = Event._DATE_PATTERN.match(text)
if not match:
raise ValueError(f"expected YYYY-MM-DD, got {text!r}")
return date(*map(int, match.groups()))
class Meeting(Event):
pass
launch = Event.from_string("Product launch @ 2026-11-03")
standup = Meeting.from_dict({"title": "Standup", "day": "2026-09-22"})
print(launch, standup)
print(launch.days_until(date(2026, 9, 21)))
print(Event.parse_date("2026-12-25"))
Event('Product launch', 2026-11-03) Meeting('Standup', 2026-09-22)
43
2026-12-25
The class has all three kinds of method, each for a reason:
days_untilneeds a specific event's date, so it's an instance method.from_stringandfrom_dictcreate events from other formats. They callcls(...), notEvent(...), which is the key detail. When called asMeeting.from_dict(...),clsisMeeting, so you get aMeetingback withoutMeetinghaving to redefine anything. TheSelfreturn type (fromtyping, Python 3.11+) tells type checkers the same thing.parse_dateturns a string into adate. It doesn't need the class or an instance; it's a self-contained helper that belongs withEventbecause that's the only place it's used.
The naming convention from_<source> is worth following. The standard library uses it all over the place: dict.fromkeys(), datetime.fromisoformat(), datetime.fromtimestamp(), int.from_bytes(), and date.today() are all class methods that act as alternative constructors.
Alternative constructors keep __init__ simple. Instead of one initializer with a tangle of optional parameters for every input format, __init__ takes the canonical values, and each class method handles one way of producing them. If parsing fails, the error happens before an object exists.
Why cls Beats a Hardcoded Class Name
You might wonder why from_dict isn't a static method that returns Event(...). Here's what happens when you try that with inheritance:
class Pizza:
def __init__(self, toppings: list[str]) -> None:
self.toppings = toppings
def __repr__(self) -> str:
return f"{type(self).__name__}({self.toppings})"
@staticmethod
def margherita_static() -> "Pizza":
return Pizza(["tomato", "mozzarella"])
@classmethod
def margherita(cls) -> "Pizza":
return cls(["tomato", "mozzarella"])
class GlutenFreePizza(Pizza):
pass
print(GlutenFreePizza.margherita_static())
print(GlutenFreePizza.margherita())
Pizza(['tomato', 'mozzarella'])
GlutenFreePizza(['tomato', 'mozzarella'])
The static version always builds a Pizza, even when you ask the subclass for one, which is a subtle bug. The class method builds whatever class it was called on. Any method that creates instances of its own class should be a class method.
Class Methods for Class-Level State
The second use for class methods is working with data that belongs to the class as a whole rather than any one instance: counters, registries, shared configuration.
class Connection:
_open_count = 0
max_connections = 2
def __init__(self, host: str) -> None:
type(self)._register()
self.host = host
@classmethod
def _register(cls) -> None:
if cls._open_count >= cls.max_connections:
raise RuntimeError(f"{cls.__name__}: too many open connections")
cls._open_count += 1
@classmethod
def open_count(cls) -> int:
return cls._open_count
a = Connection("db1")
b = Connection("db2")
print(Connection.open_count())
Connection("db3")
2
RuntimeError: Connection: too many open connections
Writing cls._open_count += 1 updates the attribute on the class. Doing the same thing through self in an instance method is a classic bug:
class Counter:
count = 0
def bump_wrong(self) -> None:
self.count += 1 # creates an instance attribute, class untouched
@classmethod
def bump(cls) -> None:
cls.count += 1
c = Counter()
c.bump_wrong()
print(Counter.count, c.count)
Counter.bump()
print(Counter.count, Counter().count)
0 1
1 1
self.count += 1 reads the class attribute but writes a new instance attribute on c, leaving the class's counter at zero. The class method updates the shared value.
One thing to keep in mind: with inheritance, cls._open_count += 1 called on a subclass creates a separate counter on that subclass. Sometimes that's what you want (per-subclass counts); if it isn't, name the base class explicitly: Connection._open_count += 1.
Static Methods: Helpers in the Class Namespace
A static method is just a function. It receives no special arguments and can't affect the class or instances unless you pass them in. So why put it in the class at all?
- Grouping.
Event.parse_dateis clearly tied to events, and anyone reading the class sees it there. - Overridability. Because it's looked up through the class (
self.parse_date(...)orcls.parse_date(...)), a subclass can override it with a different implementation. - Discoverability. It shows up in IDE autocomplete on the class and in
help(Event).
That said, Python has modules, and a module-level function is often the better home. Ask: does anything other than this class need this function? If so, move it out. A parse_date that's useful across the codebase belongs in a dates.py module, not on Event. The Python community leans toward module-level functions more than languages like Java, where every function must live in a class.
A useful smell test: a class with only static methods should be a module. class StringUtils with ten static methods is a Java-ism. In Python, make it string_utils.py with ten functions. See what are Python modules and packages for how to organize that.
Also, note that parse_date refers to Event._DATE_PATTERN by name. That's the main limitation of static methods: when they need class data, they have to hard-code the class. If you find yourself doing that a lot, it probably wants to be a class method instead.
How to Choose
Work through these questions in order:
- Does the method need data from a specific instance? Use an instance method. (This is most methods.)
- Does it create an instance of the class, or work with class-level data? Use a class method, and use
clsrather than the class name. - Is it a helper that's specific to this class but uses neither? A static method is fine.
- Is it a general utility that other code could use too? Make it a module-level function.
Some quick examples:
| Method | Kind |
|---|---|
order.add_item(product) | Instance |
order.total() | Instance |
Order.from_json(text) | Class method |
Order.from_row(db_row) | Class method |
Plugin.registered() (list subclasses) | Class method |
Order.validate_sku(sku) | Static method |
slugify(text) used everywhere | Module-level function |
Inheritance and Overriding
All three kinds can be overridden in subclasses, and calls through self or cls pick up the override:
- Instance methods dispatch on
type(self), as you'd expect. - Class methods receive the subclass as
cls, so overridden class attributes and constructors are used. Inside a class method you can callsuper().from_dict(data)to extend the parent's version, andclsis still the subclass. - Static methods can be overridden too, but only calls that go through
self.orcls.see the override. A call that names the class directly, likeEvent.parse_date(...), always gets the base version.
For more on how super() finds the next method, see inheritance and super() in Python.
A Few Details
- Names are conventions.
selfandclsaren't keywords, but every Python developer expects them. Don't rename them. - Decorator order matters when stacking.
@classmethodand@staticmethodshould usually be the outermost (top) decorator. Chaining@classmethodwith@propertyto make a class-level property was deprecated in Python 3.11 and no longer works in 3.13. - Static methods are callable directly. Since Python 3.10, a
staticmethodobject is itself callable, so you can call it from within the class body during class creation. Rarely needed, but it removes an old surprise. - Abstract methods combine with both. You can stack
@classmethodor@staticmethodon top of@abstractmethodto require subclasses to implement them; see abstract base classes.
Conclusion
The three kinds of method differ only in what they receive first: instance methods get the object, class methods get the class, and static methods get nothing. Use instance methods for almost everything. Use class methods for alternative constructors named from_<something> and for class-wide state, and always build instances with cls(...) so subclasses work. Use static methods sparingly, for helpers that clearly belong to the class, and move anything more general out to a module-level function. Once you think in terms of "what does this method need?", the right decorator is usually obvious.


