The @classmethod decorator transforms a method into a class method that receives the class object (cls) as its first implicit argument rather than an instance (self).
Factory Constructors & Shared State: Because class methods bind directly to the class, they can access and mutate class-level attributes, track global object registries, and serve as polymorphic alternative constructors (such as creating instances from JSON, CSV, or formatted strings). Unlike @staticmethod, which receives no implicit reference, @classmethod maintains full access to the class hierarchy. For related OOP capabilities, see classes & objects and isinstance().
The @classmethod decorator can be applied via decorator syntax:
class Example:
@classmethod
def my_class_method(cls, *args, **kwargs):
return cls
| Parameter | Type Accepted | Behavioral Details |
|---|---|---|
cls |
type (class object) |
Implicit first parameter automatically bound to the calling class. |
*args, **kwargs |
Any arguments | Arguments passed when invoking the class method. |
| Invocation | Class or instance | Can be called as Example.method() or instance.method(). Both pass cls. |
Class methods access class-level data without needing an instantiated object:
class Animal:
home = "Jungle"
def __init__(self, name: str):
self.name = name
@classmethod
def describe_habitat(cls):
print(f"All {cls.__name__} reside in the {cls.home}.")
# 1. Invoke on class directly
Animal.describe_habitat()
# 2. Invoke through instance
tiger = Animal("Ronald")
tiger.describe_habitat()
Output:
All Animal reside in the Jungle.
All Animal reside in the Jungle.
Python classes support three distinct categories of methods:
| Method Type | Decorator | First Parameter | Primary Role |
|---|---|---|---|
| Instance Method | None | self (instance) |
Operates on individual object state. |
| Class Method | @classmethod |
cls (class) |
Alternative constructors, class state. |
| Static Method | @staticmethod |
None (plain args) | Self-contained utility functions. |
class Demo:
def inst_method(self):
return "Instance method (self)"
@classmethod
def cls_method(cls):
return f"Class method ({cls.__name__})"
@staticmethod
def stat_method():
return "Static method (standalone)"
d = Demo()
print(d.inst_method())
print(Demo.cls_method())
print(Demo.stat_method())
Output:
Instance method (self)
Class method (Demo)
Static method (standalone)
Class methods excel at creating alternative constructors from strings or parsed dictionaries:
class Date:
def __init__(self, year: int, month: int, day: int):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str: str):
y, m, d = map(int, date_str.split("-"))
return cls(y, m, d)
d1 = Date(2026, 10, 14)
d2 = Date.from_string("2026-10-14")
print("Standard:", d1.year, d1.month, d1.day)
print("Factory: ", d2.year, d2.month, d2.day)
Output:
Standard: 2026 10 14
Factory: 2026 10 14
Because factory methods invoke cls(...), calling the inherited factory on a subclass properly returns a subclass instance:
class Document:
def __init__(self, title: str):
self.title = title
@classmethod
def from_title(cls, title: str):
return cls(title)
class Report(Document):
pass
rep = Report.from_title("Q3 Financials")
print("Object type:", type(rep))
print("Is Report: ", isinstance(rep, Report))
Output:
Object type: <class '__main__.Report'>
Is Report: True
Class methods can access and update shared class-level attributes across all instances:
class UserRegistry:
user_count = 0
def __init__(self, username: str):
self.username = username
UserRegistry.user_count += 1
@classmethod
def get_total_users(cls) -> int:
return cls.user_count
u1 = UserRegistry("Alice")
u2 = UserRegistry("Bob")
print("Total users:", UserRegistry.get_total_users())
Output:
Total users: 2
Cleanly construct data models from parsed API payloads:
class UserProfile:
def __init__(self, uid: int, email: str):
self.uid = uid
self.email = email
@classmethod
def from_dict(cls, data: dict):
return cls(data["id"], data["email"])
profile = UserProfile.from_dict({"id": 101, "email": "dev@plus2net.com"})
print("Profile:", profile.uid, profile.email)
Output:
Profile: 101 dev@plus2net.com
Automatically track subclasses across your application:
class PluginRegistry:
plugins = {}
@classmethod
def register(cls, subclass):
cls.plugins[subclass.__name__] = subclass
return subclass
@PluginRegistry.register
class JsonFormatter: pass
print("Plugins:", list(PluginRegistry.plugins.keys()))
Output:
Plugins: ['JsonFormatter']
cls(...) rather than the parent class name MyClass(...). Hardcoding the parent breaks subclass polymorphism.
Practice class methods, alternative constructors, and registries in our companion Jupyter Notebook:
Explore interactive code cells, build alternative factory constructors, compare class vs static methods, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify @classmethod mechanics:
def test_classmethod_mechanics():
class Vehicle:
def __init__(self, speed: int):
self.speed = speed
@classmethod
def create_fast(cls):
return cls(120)
class Car(Vehicle):
pass
v = Vehicle.create_fast()
c = Car.create_fast()
assert v.speed == 120
assert type(v) is Vehicle
assert type(c) is Car
print("All @classmethod unit assertions passed! 100% verified.")
test_classmethod_mechanics()
Output:
All @classmethod unit assertions passed! 100% verified.
Defines methods that receive neither an implicit instance (self) nor class (cls) argument.
Creates managed getter, setter, and deleter methods disguised as regular attributes.
Verifies whether an instantiated object is an instance of a specified class.
Checks if a class derives from another class or tuple of classes.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.