Working with Instance, Class and Static Methods
November 21, 2021
Overview
Python's instance, class and static methods allow you to build intuitive object-orientated classes that help convey developer intent. An example class containing all method types looks like this:
class Methods:
def instance_method(self):
...
@classmethod
def class_method(cls):
...
@staticmethod
def static_method():
...
Instance Method
- The default method of a class (does not use any decorator)
- Receives the caller object as a special first parameter (usually named
self
, but this can be anything you want) - Bound to the instance of the class (requires a class object to be created)
- Has access to class state and instance state
- Usage:
- Useful when you need access to class state and instance state
Class Method
- Uses the
@classmethod
decorator - Receives the caller class as a special first parameter (usually named
cls
, but this can be anything you want) - Bound to the class (doesn't require a class object to be created)
- Has access to class state but not instance state
- Usage:
- Useful when you need access to class state only
- Help create instances of a class with pre-defined characteristics (alternative constructors)
- Automatically use the sub-class in an inheritance scenario (remember, a class method receives the caller class)
Static Method
- Uses the
@staticmethod
decorator - Does not receive the caller object or the caller class as a special first parameter
- Not bound to the instance of the class or the class itself (doesn't require a class object to be created)
- Does not have access to class state or instance state
- Usage:
- Useful to communicate developer intent that the method won't modify class or instance state
- Couple the method to the class namespace
- Perform generic / default operations
- Testing doesn't require an instance of a class, simply test the method like you would any other function
Object State and Class State
Alter State with Instance Methods
An instance method receives the caller object and may alter object state and class state.
- To alter object state, the instance method makes use of the
self
parameter - To alter class state for the instance, the instance method makes use of the
self
parameter and calls on the class attribute - To alter class state for the class, the instance method makes use of the
self
parameter and calls on the class attribute with__class__
For example:
class MyClass:
class_name = "My Class"
def __init__(self, object_name):
self.object_name = object_name
def set_object_name(self, object_name):
self.object_name = object_name
def set_class_name_for_object(self, class_name):
self.class_name = class_name
def set_class_name_for_class(self, class_name):
self.__class__.class_name = class_name
def get_names(self):
return f"class_name = {self.class_name}, object_name = {self.object_name}"
a = MyClass("Object A")
b = MyClass("Object B")
print(a.get_names())
print(b.get_names())
# alter `object_name` of object `a` ONLY
a.set_object_name("Object AAA")
print(a.get_names())
print(b.get_names())
# alter `class_name` of object `a` ONLY
a.set_class_name_for_object("My Class A")
print(a.get_names())
print(b.get_names())
# alter `class_name` of all objects
a.set_class_name_for_class("My Class A")
print(a.get_names())
print(b.get_names())
Which returns the following:
class_name = My Class, object_name = Object A
class_name = My Class, object_name = Object B
class_name = My Class, object_name = Object AAA
class_name = My Class, object_name = Object B
class_name = My Class A, object_name = Object AAA
class_name = My Class, object_name = Object B
class_name = My Class A, object_name = Object AAA
class_name = My Class A, object_name = Object B
Alter State with Class Methods
A class method receives the caller class and may alter class state.
- To alter class state, the class method makes use of the
cls
parameter and calls on the class attribute
For example:
class MyClass:
class_name = "My Class"
def __init__(self, object_name):
self.object_name = object_name
@classmethod
def set_class_name(cls, class_name):
cls.class_name = class_name
def get_names(self):
return f"class_name = {self.class_name}, object_name = {self.object_name}"
a = MyClass("Object A")
b = MyClass("Object B")
print(a.get_names())
print(b.get_names())
# alter `class_name` of all objects
a.set_class_name("My Class A")
print(a.get_names())
print(b.get_names())
Which returns the following:
class_name = My Class, object_name = Object A
class_name = My Class, object_name = Object B
class_name = My Class A, object_name = Object A
class_name = My Class A, object_name = Object B