classmethod vs staticmethod in Python

Compare classmethod and staticmethod in Python with examples.

TL;DR

Use @classmethod when the method needs to know which class it belongs to (e.g., alternate constructors, polymorphic factories, class-wide configuration). Use @staticmethod when you just want a helper function namespaced under a class and it doesn’t need instance (self) or class (cls) access.


Why these decorators exist

Python methods are just functions living on a class. When you access them, Python’s descriptor protocol decides what gets bound as the first argument:

This binding behavior is the core mental model for choosing the right decorator.


Quick definitions


Side-by-side cheat sheet

Feature Instance method Class method Static method
First arg self cls (none)
Reads instance state
Reads/changes class state ⚠️ via self.__class__
Alternate constructors
Polymorphic dispatch by subclass ⚠️ (lookup works, but no binding)
Good for utilities ⚠️
Testability Good Good Excellent (pure)

When to use @classmethod

1) Alternate constructors

Give readers an obvious way to build instances from other representations.

class User:
    def __init__(self, name, is_admin=False):
        self.name = name
        self.is_admin = is_admin

    @classmethod
    def from_env(cls):
        import os
        return cls(os.getenv("USER", "anonymous"))

    @classmethod
    def admin(cls, name):
        return cls(name, is_admin=True)

2) Polymorphic factories

Subclasses inherit and can override classmethods. Calls via the subclass bind cls to the subclass—so your factory keeps returning the right type.

class User:
    def __init__(self, name):
        self.name = name

    @classmethod
    def from_dict(cls, data):
        return cls(data["name"])

Now subclasses can reuse it and automatically return their own type:

class Admin(User):
    pass

user = User.from_dict({"name": "Alice"})
admin = Admin.from_dict({"name": "Bob"})

print(type(user))   # <class '__main__.User'>
print(type(admin))  # <class '__main__.Admin'>

3) Class-level configuration and registries

Keep shared, type-specific state on the class.

class Service:
    _registry = {}

    @classmethod
    def register(cls, name, impl):
        cls._registry[name] = impl

    @classmethod
    def get(cls, name):
        return cls._registry[name]

4) Versioned/typed constructors with typing.Self

From Python 3.11, use Self to signal “returns an instance of the calling class”.

from typing import Self

class Config:
    @classmethod
    def load(cls, path: str) -> Self:
        ...
        return cls(...)

When to use @staticmethod

1) Pure helpers that don’t touch class/instance state

Prefer module-level functions for true utilities, unless colocating them with the class improves discoverability or API ergonomics.

class Password:
    @staticmethod
    def hash(plain: str) -> str:
        import hashlib
        return hashlib.sha256(plain.encode()).hexdigest()

2) Validation or small transforms used by multiple constructors

Keep related helpers near where they’re used.

class Color:
    @staticmethod
    def _clamp(x: int) -> int:
        return max(0, min(255, x))

Rule of thumb: If the function could live at module scope without losing clarity, a @staticmethod is fine; if it benefits from knowing the class, make it a @classmethod.


Common pitfalls & how to avoid them

  1. Needing cls but using @staticmethod

    • If you might return cls(...) (alternate constructor) or read class attributes, use @classmethod.
  2. Using @classmethod just for grouping

    • If you never touch cls, you’re likely better off with a @staticmethod (or a top-level function).
  3. Confusing inheritance behavior

    • @classmethod participates in polymorphism naturally.
    • @staticmethod still follows normal attribute lookup (so you can override it), but it doesn’t get the class bound automatically.
  4. Decorator order with ABCs

    • With abc.ABC, combine as:

      from abc import ABC, abstractmethod
      
      class Repo(ABC):
          @classmethod
          @abstractmethod
          def from_url(cls, url: str): ...
      
          @staticmethod
          @abstractmethod
          def validate(url: str) -> bool: ...
      
    • (Order matters: put @classmethod / @staticmethod above @abstractmethod.)

  5. No built-in @classproperty

    • Python has @property for instances and @classmethod for methods. If you want a computed class-level property, you’ll need a small custom descriptor or a metaclass. Most of the time, a simple @classmethod like def info(cls) is clearer.

Design patterns you’ll meet in the wild


Testing tips


Decision guide

  1. Does the method need the instance? → Plain method (no decorator).
  2. Does the method need the class (to read class data, construct, or be polymorphic)?@classmethod.
  3. Neither?@staticmethod (or module-level function if discoverability isn’t a concern).

Mini FAQ


Takeaways

Happy Pythoning! 🐍