Python : @classmethod, @staticmethod and methods
In this post we will demystify the differences between @classmethod, @staticmethod and regular methods in Python. We will discuss what is the diffrence and when to use it. After reading this post you will have a better understanding on which method to use in your classes.
Let us create a single class with following methods.
# oop.py
class MyClass:
def method(self):
return 'instance method called', self
@classmethod
def classmethod(cls):
return 'class method called', cls
@staticmethod
def staticmethod():
return 'static method called'
Now first let us call the instance method. This method get the instance of the object calling it as input param and it can read/edit the attributes of that object. So this method can modify both object state and class state.
>>> myObj = oop.MyClass()
>>> myObj.method()
('instance method called', <oop.MyClass object at 0x00000228F0EBF710>)
Now let us call the classmethod, which gets the class object (In python everything is an object) as input param and use that to call the class methods. This method cannot modify the object state, but can modify the class state. We will look into a detail example below.
>>> myObj.classmethod()
('class method called', <class 'oop.MyClass'>)
A static method is just a regular function, which has no connection to the actual class, but making it static , scopes it to the class. You have to call it at class level not the object level. Its usually used to write utility functions associated with class.
>>> >>> myObj.staticmethod()
'static method called'
You can also call classmethods and staticmethods in a class level.
>>> oop.MyClass.classmethod()
('class method called', <class 'oop.MyClass'>)
>>> oop.MyClass.staticmethod()
'static method called'
In order to solidify our concept let us create a simple Pizza class.
# pizza.py
class Pizza(object):
def __init__(self,ingredients):
self.ingredients = ingredients
def __repr__(self):
return 'Pizza{}'.format(self.ingredients)
Now based on type of pizza we have to create it as shown below.
>>> margarita = Pizza(['cheese', 'tomattoes'])
>>> margarita
Pizza['cheese', 'tomattoes']
>>> prosciutto = Pizza(['cheese', 'tomattoes', 'ham', 'mushrooms'])
>>> prosciutto
Pizza['cheese', 'tomattoes', 'ham', 'mushrooms']
Consider this class has been actually using in a Pizza dealer software and everytime user adds an ingredient in their website interally we have to create with ingredients, its a pain. This is where @classmethods become handy to provide factory methods based on type of pizza.
# pizza.py
class Pizza(object):
def __init__(self,ingredients):
self.ingredients = ingredients
def __repr__(self):
return 'Pizza{}'.format(self.ingredients)
@classmethod
def margharita(cls):
return cls(['cheese','tomattoes'])
@classmethod
def prosciutto(cls):
return cls(['cheese','tomattoes', 'ham', 'mushrooms'])
Now we can use these factory methods for creating the required type of pizza as shown below.
>>> from pizza import Pizza
>>> Pizza.margharita()
Pizza['cheese', 'tomattoes']
>>> Pizza.prosciutto()
Pizza['cheese', 'tomattoes', 'ham', 'mushrooms']
So now for understanding the @staticmethods let us add a method to calculate the area of the pizza. (May be to classify as large, medium etc). I don’t know a bad example.. So only goal here to show how to write and use @static method in a class and ensure that they are written as a utility with in scope of a class rather than a public function.
# pizza.py
import math
class Pizza(object):
pi = 3.14
def __init__(self,ingredients, radius):
self.ingredients = ingredients
self.radius = radius
def __repr__(self):
return 'Pizza{}'.format(self.ingredients)
def area(self):
return self.calc_circle_area(self.radius)
@classmethod
def margharita(cls):
return cls(['cheese','tomattoes'])
@classmethod
def prosciutto(cls):
return cls(['cheese','tomattoes', 'ham', 'mushrooms'])
@staticmethod
def calc_circle_area(radius):
return math.pi * (radius**2)
>>> from pizza import Pizza
>>> margharita = Pizza.margharita()
>>> margharita.area()
314.1592653589793
So in a nutshell, use @staticmethods when you want to write a utility method with in scope of the class. @classmethods are used often when we need to have multiple independent constructors. (factory methods).
Using @classmethod as factory
Another example of using @classmethod to provide a second constructor or factory method.
Let us consider we have a custom date class “MyDate” as shown below.
class MyDate:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year
def __str__(self):
return '{}/{}/{}'.format(self.day,
self.month, self.year)
Now let us consider we need to create MyDate from a string representation. In this case we can add a new factory method using @classmethod as shown below.
class MyDate:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year
def __str__(self):
return '{}/{}/{}'.format(self.day, self.month, self.year)
@classmethod
def create_date_from_string(cls, str_date):
'''
convert a string date (day-mon-year) to mydate
:return:
'''
day, mon, year = str_date.split('-')
return cls(day, mon, year)
Let us use it.
mydate = MyDate.create_date_from_string("12-12-79")
print(mydate)
# 12/12/79
Coding is fun enjoy…