@staticmethod vs @classmethod
@staticmethod behaves like a plain function with no automatic argument, while @classmethod receives the class as cls and usually serves as an alternative constructor.
Side-by-side comparison
#First argument
- @staticmethod
- None
- @classmethod
cls(the class)
Access to class attributes
- @staticmethod
- No, unless through a hardcoded class name
- @classmethod
- Yes, through
cls
Creating an object through cls(...)
- @classmethod
- Yes, this is the typical use
Behavior in a derived class
- @staticmethod
- Not applicable, no reference to the class
- @classmethod
clsis the derived class, not the base one
Calling it
- @staticmethod
Class.method(args)- @classmethod
Class.method(args)
Typical use
- @staticmethod
- Validation, calculations independent of object data
- @classmethod
- Alternative constructors (from_text, from_dict)
Example
#class Grade:
def __init__(self, points):
self.points = points
@staticmethod
def is_valid(points):
return 0 <= points <= 100
@classmethod
def from_percent(cls, percent, maximum):
return cls(round(percent / 100 * maximum))
print(Grade.is_valid(120))
grade = Grade.from_percent(75, 80)
print(grade.points)Definition and usage
#Both decorators define a method you can call through the class name, without creating an object. Neither one receives self, so neither has access to a specific object’s data.
They differ in their first argument. A static method gets nothing automatically: it’s a plain function, just placed inside the class for organization. A class method gets the class itself (cls) as its first argument, which lets it reference the class’s attributes and, more importantly, create an object through cls(...). In a derived class, cls points to that derived class, not to the class where the method was written.
Use @staticmethod for a helper function that logically belongs to the class, e.g. validation or a calculation, but needs neither an object nor the class. Use @classmethod when the method should create objects from data in a different format than the regular constructor, e.g. from_text() or from_dict(), or when it needs access to class attributes, such as a shared counter.
Syntax
#class Name:
@staticmethod
def method(arg):
...
@classmethod
def other(cls, arg):
return cls(arg)More examples
#class Task:
def __init__(self, number):
self.number = number
@classmethod
def from_code(cls, code):
return cls(code.removeprefix("task_"))
class Quiz(Task):
pass
task = Quiz.from_code("task_7_3")
print(type(task).__name__, task.number)class Course:
count = 0
def __init__(self, title):
self.title = title
Course.count += 1
@staticmethod
def slugify(title):
return title.lower().replace(" ", "-")
python_course = Course("Python from scratch")
print(Course.slugify(python_course.title))
print(Course.count)Good practices
#- If a method uses neither
selfnorcls, it probably should be static, or not in the class at all. - Name alternative constructors with a
from_prefix, e.g.from_json(),from_row(). It’s a convention familiar from the standard library. - In a class method, create objects through
cls(...), not a hardcoded class name, so it also works correctly in derived classes.
Related entries
#- @staticmethodA method that gets neither the object nor the class automatically. It behaves like a plain function placed inside a class.
- @classmethodA method that receives the class (cls) instead of an object. It is most often used as an alternative constructor.
- selfThe first parameter of a method: the object the method was called on. It gives access to that object’s attributes and other methods.
- classDefines a class: a template for objects that combines data with the functions that act on it.
- DecoratorsFunctions that wrap other functions and add behavior to them, such as logging or permission checks. Written as @name.
Spotted a mistake or missing an example? Write to us.