Meaning of @classmethod and @staticmethod for beginner?
Solution 1
Though classmethod
and staticmethod
are quite similar, there's a slight difference in usage for both entities: classmethod
must have a reference to a class object as the first parameter, whereas staticmethod
can have no parameters at all.
Example
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')
Explanation
Let's assume an example of a class, dealing with date information (this will be our boilerplate):
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
This class obviously could be used to store information about certain dates (without timezone information; let's assume all dates are presented in UTC).
Here we have __init__
, a typical initializer of Python class instances, which receives arguments as a typical instancemethod
, having the first non-optional argument (self
) that holds a reference to a newly created instance.
Class Method
We have some tasks that can be nicely done using classmethod
s.
Let's assume that we want to create a lot of Date
class instances having date information coming from an outer source encoded as a string with format 'dd-mm-yyyy'. Suppose we have to do this in different places in the source code of our project.
So what we must do here is:
- Parse a string to receive day, month and year as three integer variables or a 3-item tuple consisting of that variable.
- Instantiate
Date
by passing those values to the initialization call.
This will look like:
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)
For this purpose, C++ can implement such a feature with overloading, but Python lacks this overloading. Instead, we can use classmethod
. Let's create another "constructor".
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
date2 = Date.from_string('11-09-2012')
Let's look more carefully at the above implementation, and review what advantages we have here:
- We've implemented date string parsing in one place and it's reusable now.
- Encapsulation works fine here (if you think that you could implement string parsing as a single function elsewhere, this solution fits the OOP paradigm far better).
-
cls
is an object that holds the class itself, not an instance of the class. It's pretty cool because if we inherit ourDate
class, all children will havefrom_string
defined also.
Static method
What about staticmethod
? It's pretty similar to classmethod
but doesn't take any obligatory parameters (like a class method or instance method does).
Let's look at the next use case.
We have a date string that we want to validate somehow. This task is also logically bound to the Date
class we've used so far, but doesn't require instantiation of it.
Here is where staticmethod
can be useful. Let's look at the next piece of code:
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
# usage:
is_date = Date.is_date_valid('11-09-2012')
So, as we can see from usage of staticmethod
, we don't have any access to what the class is---it's basically just a function, called syntactically like a method, but without access to the object and its internals (fields and another methods), while classmethod does.
Solution 2
Rostyslav Dzinko's answer is very appropriate. I thought I could highlight one other reason you should choose @classmethod
over @staticmethod
when you are creating an additional constructor.
In the example, Rostyslav used the @classmethod
from_string
as a Factory to create Date
objects from otherwise unacceptable parameters. The same can be done with @staticmethod
as is shown in the code below:
class Date:
def __init__(self, month, day, year):
self.month = month
self.day = day
self.year = year
def display(self):
return "{0}-{1}-{2}".format(self.month, self.day, self.year)
@staticmethod
def millenium(month, day):
return Date(month, day, 2000)
new_year = Date(1, 1, 2013) # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object.
# Proof:
new_year.display() # "1-1-2013"
millenium_new_year.display() # "1-1-2000"
isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True
Thus both new_year
and millenium_new_year
are instances of the Date
class.
But, if you observe closely, the Factory process is hard-coded to create Date
objects no matter what. What this means is that even if the Date
class is subclassed, the subclasses will still create plain Date
objects (without any properties of the subclass). See that in the example below:
class DateTime(Date):
def display(self):
return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)
datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)
isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False
datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class for more details.
datetime2
is not an instance of DateTime
? WTF? Well, that's because of the @staticmethod
decorator used.
In most cases, this is undesired. If what you want is a Factory method that is aware of the class that called it, then @classmethod
is what you need.
Rewriting Date.millenium
as (that's the only part of the above code that changes):
@classmethod
def millenium(cls, month, day):
return cls(month, day, 2000)
ensures that the class
is not hard-coded but rather learnt. cls
can be any subclass. The resulting object
will rightly be an instance of cls
.
Let's test that out:
datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)
isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True
datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"
The reason is, as you know by now, that @classmethod
was used instead of @staticmethod
Solution 3
@classmethod
means: when this method is called, we pass the class as the first argument instead of the instance of that class (as we normally do with methods). This means you can use the class and its properties inside that method rather than a particular instance.
@staticmethod
means: when this method is called, we don't pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).
Solution 4
When to use each
@staticmethod
function is nothing more than a function defined inside a class. It is callable without instantiating the class first. It’s definition is immutable via inheritance.
- Python does not have to instantiate a bound-method for object.
- It eases the readability of the code: seeing @staticmethod, we know that the method does not depend on the state of object itself;
@classmethod
function also callable without instantiating the class, but its definition follows Sub class, not Parent class, via inheritance, can be overridden by subclass. That’s because the first argument for @classmethod
function must always be cls (class)
.
- Factory methods, that are used to create an instance for a class using for example some sort of pre-processing.
- Static methods calling static methods: if you split a static methods in several static methods, you shouldn't hard-code the class name but use class methods
here is good link to this topic.
Solution 5
Meaning of
@classmethod
and@staticmethod
?
- A method is a function in an object's namespace, accessible as an attribute.
- A regular (i.e. instance) method gets the instance (we usually call it
self
) as the implicit first argument. - A class method gets the class (we usually call it
cls
) as the implicit first argument. - A static method gets no implicit first argument (like a regular function).
when should I use them, why should I use them, and how should I use them?
You don't need either decorator. But on the principle that you should minimize the number of arguments to functions (see Clean Coder), they are useful for doing just that.
class Example(object):
def regular_instance_method(self):
"""A function of an instance has access to every attribute of that
instance, including its class (and its attributes.)
Not accepting at least one argument is a TypeError.
Not understanding the semantics of that argument is a user error.
"""
return some_function_f(self)
@classmethod
def a_class_method(cls):
"""A function of a class has access to every attribute of the class.
Not accepting at least one argument is a TypeError.
Not understanding the semantics of that argument is a user error.
"""
return some_function_g(cls)
@staticmethod
def a_static_method():
"""A static method has no information about instances or classes
unless explicitly given. It just lives in the class (and thus its
instances') namespace.
"""
return some_function_h()
For both instance methods and class methods, not accepting at least one argument is a TypeError, but not understanding the semantics of that argument is a user error.
(Define some_function
's, e.g.:
some_function_h = some_function_g = some_function_f = lambda x=None: x
and this will work.)
dotted lookups on instances and classes:
A dotted lookup on an instance is performed in this order - we look for:
- a data descriptor in the class namespace (like a property)
- data in the instance
__dict__
- a non-data descriptor in the class namespace (methods).
Note, a dotted lookup on an instance is invoked like this:
instance = Example()
instance.regular_instance_method
and methods are callable attributes:
instance.regular_instance_method()
instance methods
The argument, self
, is implicitly given via the dotted lookup.
You must access instance methods from instances of the class.
>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>
class methods
The argument, cls
, is implicitly given via dotted lookup.
You can access this method via an instance or the class (or subclasses).
>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>
static methods
No arguments are implicitly given. This method works like any function defined (for example) on a modules' namespace, except it can be looked up
>>> print(instance.a_static_method())
None
Again, when should I use them, why should I use them?
Each of these are progressively more restrictive in the information they pass the method versus instance methods.
Use them when you don't need the information.
This makes your functions and methods easier to reason about and to unittest.
Which is easier to reason about?
def function(x, y, z): ...
or
def function(y, z): ...
or
def function(z): ...
The functions with fewer arguments are easier to reason about. They are also easier to unittest.
These are akin to instance, class, and static methods. Keeping in mind that when we have an instance, we also have its class, again, ask yourself, which is easier to reason about?:
def an_instance_method(self, arg, kwarg=None):
cls = type(self) # Also has the class of instance!
...
@classmethod
def a_class_method(cls, arg, kwarg=None):
...
@staticmethod
def a_static_method(arg, kwarg=None):
...
Builtin examples
Here are a couple of my favorite builtin examples:
The str.maketrans
static method was a function in the string
module, but it is much more convenient for it to be accessible from the str
namespace.
>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'
The dict.fromkeys
class method returns a new dictionary instantiated from an iterable of keys:
>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}
When subclassed, we see that it gets the class information as a class method, which is very useful:
>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'>
My advice - Conclusion
Use static methods when you don't need the class or instance arguments, but the function is related to the use of the object, and it is convenient for the function to be in the object's namespace.
Use class methods when you don't need instance information, but need the class information perhaps for its other class or static methods, or perhaps itself as a constructor. (You wouldn't hardcode the class so that subclasses could be used here.)
![Nicolas Gervais](https://i.stack.imgur.com/L7f8w.jpg?s=256&g=1)
Nicolas Gervais
I'm a data scientist at Explor.ai (you can book me as a consultant). I mostly just follow the tensorflow tag. Here are my achievements: I'm in the Top keras Answerers of all time python gold badge. I'm one of 22 with the tensorflow Silver Badge. About me: I drive a C 63 AMG :)
Updated on July 08, 2022Comments
-
Nicolas Gervais almost 2 years
Could someone explain to me the meaning of
@classmethod
and@staticmethod
in python? I need to know the difference and the meaning.As far as I understand,
@classmethod
tells a class that it's a method which should be inherited into subclasses, or... something. However, what's the point of that? Why not just define the class method without adding@classmethod
or@staticmethod
or any@
definitions?tl;dr: when should I use them, why should I use them, and how should I use them?
-
Shailyn Ortiz over 6 yearsyou can still achieve the same outcome if you create those methods without classmethod, this is not the real difference
-
abalter over 6 yearsThat was fantastically clear with just the right balance of succinct explanation and micro-examples.
-
TheRealFakeNews over 5 years
Use class methods when you don't need instance information, but need the class information perhaps for its other class or static methods, or perhaps itself as a constructor. (You wouldn't hardcode the class so that subclasses could be used here.)
Precisely what I was looking for. The actual why-I-would-use-it answer. -
t3chb0t over 5 yearsSaying that it's actually a factory method was the most helpful thing in explaining what
@classmethod
is for. -
Bahubali Patil about 5 yearsThe "from_string" method takes "Date" class (not Date object) as the first parameter "cls" and returns the constructor by calling cls(day, month, year), which is equivalent to Date(day, month, year) and returns a Date object.
-
piby180 almost 5 yearsThis is the best explanation. Thanks!
-
John R Perry almost 5 yearsThank you for getting to the point wayyyy faster than the accepted answer.
-
Cryptoharf84 almost 5 yearsso can class methods be regarded as "alternative constructors"? I think this is the best explanation so far!
-
rishi jain over 4 yearsWhat is cls? As per answer above - cls can be any subclass. The resulting object will rightly be an instance of cls. Is cls an object or method calling Date or Datetime? Please explain.
-
Sibs about 4 years@rishijain Like how self refers to the instance, cls refers to the class - e.g. cls(month, day, 2000) == DateTime(month, day, 2000)
-
Josh about 4 yearsRegarding your
classmethod
example, wouldn't you be able to do exactly what you did above by definingfrom_string
as astringmethod
instead of asclassmethod
, and then, instead of callingcls(day, month, year)
you would callDate(day, month, year)
? I suppose that the only benefit of using aclassmethod
here would be if you would like that method to be available to subclasses who may inherit and expectfrom_string
to work for the inherited class right? Or did I miss anything your point? -
DarrylG about 4 years@Josh--stringmethod needs an existing object to work off of so you couldn't do the same thing. As Crypoharf84 mentions, this allows for an alternative mechanism of constructing an object. This seems analogous to "named constructors" in the Dart language which provides this mechanism for the creation of multiple constructors for the same class. Different constructors allow easy API creation such as Date.from_json, Date.from_sql, Date.from_file, etc.
-
Christabella Irwanto almost 4 yearsThis answer really clarifies the utility of
@classmethod
for factory methods that support inheritance: creating an instance of the calling class with some pre-processing etc. -
Sia over 3 years@DarrylG @Josh what is a
stringmethod
? Are you guys talking aboutstaticmethod
? If so, I still don't understand why you can't usestaticmethod
to accomplish the same thing as @Josh suggests. -
hafiz031 over 2 yearsIf some method is defined inside the class but before
__init__()
method, will it be equivalent toclass method
orstatic method
or some other else? -
Xenty about 2 years@hafiz031 For anyone still interested in an answer to this question, any method defined inside the class before
__init__()
is still a normalinstancemethod
. The order of the functions inside the class definition does not matter. You can put the__init__()
as the last function and everything will still work the same. Usually, I would recommend putting it as the first function though, because anyone looking at the code will be able to immediately see how an instance of your class can be created. -
Sam about 2 yearsThis is a very gooood answer! Perfectly explains the concept. I'm glad to bump your rep up to 10k.
-
PirateApp about 2 yearswhy havent I seen this in Javascript, why is Python the only language that requires a separate @classmethod and @staticmethod?