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.)