Python supports three types of methods in class definitions: instance methods, class methods, and static methods. They differ in how they are called and their access to class and instance data.
History:
Initially, instance methods (those with "self") were the only type of behavior; it was implied that methods always invoke behavior or read/change data of a specific object. Later, class methods (with "cls") were introduced in Python, providing behavior for the class as a whole (e.g., alternative constructors), and static methods, which are similar to regular functions but associated with the class.
Problem:
Sometimes common functionality is required for all instances (static methods). Other times, an operation should be available "for the entire class" (e.g., creating an instance). If the method type is incorrectly defined, bugs may arise (for example, accidentally trying to modify the class through an instance method or vice versa).
Solution:
Code example:
class MyClass: def instance_method(self): return f"Instance: {self}" @classmethod def class_method(cls): return f"Class: {cls}" @staticmethod def static_method(): return "This is a static method" obj = MyClass() print(obj.instance_method()) # Instance: <MyClass object...> print(MyClass.class_method()) # Class: <class 'MyClass' > print(MyClass.static_method())# This is a static method
Key features:
Can an instance method be called directly through the class?
Yes, but the object must be explicitly passed:
MyClass.instance_method(obj)
which is rarely appropriate.
Behavior of static methods in inheritance: can they be overridden?
Yes, you can declare a staticmethod in the child class with the same name, and that one will be called when accessed from the descendant.
Why use class methods if static methods with cls as an argument can always be used?
cls is not just the first argument: in classmethod, Python automatically provides the appropriate class for cls even if the call is made from a descendant. This allows creating an alternative hierarchy of constructors without being closely tied to the parent.
Example:
class Base: @classmethod def make(cls): return cls() class Child(Base): pass Child.make() # will return Child, not Base
In a project, regular methods were used for creating alternative instances (e.g., create_from_json). As a result, when inherited, the method always returned an object of the base class, not the descendant.
Pros:
Cons:
Classmethod factories were implemented, which return instances of the current class (cls()), even when called from the descendant.
Pros:
Cons: