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:

  • @classmethod receives the class (conventionally cls) instead of an instance. Its killer use is the alternative constructor.
  • @staticmethod receives 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 15

Why 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

Example · python
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
# Staff

When 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.

Example · python
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-01

staticmethod for pure utility

Groups two pure utility functions in a class with @staticmethod; they need no self or cls.

Example · python
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.0

classmethod with inheritance

Shows that cls in a classmethod binds to the class it is called on, so Dog.create returns a Dog.

Example · python
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

  • Be the first to comment on this lesson.