Class Methods & Static Methods
@classmethod for alternative constructors, @staticmethod for related helpers that need no self.
Not every method needs self. Python gives you two decorators for methods that relate to a class rather than to one instance:
@classmethodreceives the class (conventionallycls) instead of an instance. Its killer use is the alternative constructor.@staticmethodreceives nothing automatic — it is a plain function that lives in the class's namespace because it belongs there logically.
class Date:
def __init__(self, y, m, d):
self.y, self.m, self.d = y, m, d
@classmethod
def from_string(cls, text): # 'YYYY-MM-DD' -> Date
y, m, d = map(int, text.split('-'))
return cls(y, m, d) # cls, so subclasses work too
d = Date.from_string('2026-07-15')
print(d.y, d.m, d.d) # 2026 7 15Why cls and not the class name?
Using cls(...) instead of hard-coding Date(...) means a subclass calling from_string gets an instance of itself, not of the parent. That is the difference between a factory that works with inheritance and one that quietly breaks it.
Example
class User:
def __init__(self, name, role='member'):
self.name = name
self.role = role
# Alternative constructors — note cls(...) keeps subclasses happy
@classmethod
def from_dict(cls, data):
return cls(data['name'], data.get('role', 'member'))
@classmethod
def admin(cls, name):
return cls(name, role='admin')
# A pure helper that belongs to User but needs no state
@staticmethod
def is_valid_name(name):
return name.isalpha() and len(name) >= 2
def __repr__(self):
return f'User({self.name!r}, {self.role!r})'
print(User.from_dict({'name': 'Ada', 'role': 'owner'}))
print(User.admin('Grace'))
print('valid name?', User.is_valid_name('Al')) # True
print('valid name?', User.is_valid_name('X')) # False
# Subclass inherits the factories, and gets ITS OWN type back
class Staff(User):
pass
print(type(Staff.admin('Bob')).__name__) # Staff, not User
# Output:
# User('Ada', 'owner')
# User('Grace', 'admin')
# valid name? True
# valid name? False
# StaffWhen to use it
- A Date class provides a @classmethod from_string('2024-05-01') as an alternative constructor.
- A MathUtils class groups @staticmethod helpers like clamp() that do not need class or instance state.
- A Registry class uses @classmethod to maintain a shared class-level catalogue of registered plugins.
More examples
classmethod as alternative constructor
Provides from_string() as an alternative constructor via @classmethod, using cls to create the instance.
class Date:
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_string(cls, s):
y, m, d = map(int, s.split('-'))
return cls(y, m, d)
def __repr__(self):
return f'{self.year}-{self.month:02d}-{self.day:02d}'
print(Date.from_string('2024-05-01')) # 2024-05-01staticmethod for pure utility
Groups two pure utility functions in a class with @staticmethod; they need no self or cls.
class MathUtils:
@staticmethod
def clamp(value, lo, hi):
return max(lo, min(value, hi))
@staticmethod
def lerp(a, b, t):
return a + (b - a) * t
print(MathUtils.clamp(150, 0, 100)) # 100
print(MathUtils.lerp(0, 10, 0.3)) # 3.0classmethod with inheritance
Shows that cls in a classmethod binds to the class it is called on, so Dog.create returns a Dog.
class Animal:
@classmethod
def create(cls, name):
return cls(name)
def __init__(self, name): self.name = name
def __repr__(self): return f'{type(self).__name__}({self.name!r})'
class Dog(Animal): pass
print(Animal.create('Generic')) # Animal('Generic')
print(Dog.create('Rex')) # Dog('Rex')
Discussion