class method vs static method 2021
Python has three types of methods (normal, class, and static).
It's confusing and sometimes we're wondering why do we need all of them.
Even, there are people saying we do not need static method at all and recommend not using it.
The normal and class method requires passing in the first argument:
- self for normal method
- cls for class method
- but a static method requires none to be passed
We can check the behavior of each method regarding the first argument each method takes:
class MyClass: def __init__(self): pass def normal_method(*args,**kwargs): print("normal_method({0},{1})".format(args,kwargs)) @classmethod def class_method(*args,**kwargs): print("class_method({0},{1})".format(args,kwargs)) @staticmethod def static_method(*args,**kwargs): print("static_method({0},{1})".format(args,kwargs)) obj = MyClass() obj.normal_method() obj.normal_method(1,2,a=3,b=4) obj.class_method() obj.class_method(1,2,a=3,b=4) obj.static_method() obj.static_method(1,2,a=3,b=4)
normal_method((<__main__.MyClass object at 0x10d06c370>,),{}) normal_method((<__main__.MyClass object at 0x10d06c370>, 1, 2),{'a': 3, 'b': 4}) class_method((<class '__main__.MyClass'>,),{}) class_method((<class '__main__.MyClass'>, 1, 2),{'a': 3, 'b': 4}) static_method((),{}) static_method((1, 2),{'a': 3, 'b': 4})
So, a static method doesn't have access to self or cls.
The static method works like normal function but somehow belongs to the class:
static method usually does not use variables defined in the class but lots of the times
we just want to put the method into class definition because it has logical link (loosely coupled) to the class.
It also gives us maintenance benefits by just being defined in the class.
The static methods are used to do some utility tasks, and class methods are used for factory methods. The factory methods can return class objects for different use cases.
Class Method | Static Method |
---|---|
Takes cls (class) as first argument. | Does not take any specific parameter. |
Can access and modify the class state. | Cannot access or modify the class state. |
Takes the class as parameter to know about the state of that class. | Does not know about class state and is used to do some utility tasks by taking some parameters. |
@classmethod decorator is used. | @staticmethod decorator is used. |
Q: What statement about static methods is true?
- Static methods are called static because they always return None.
- Static methods can be bound to either a class or an instance of a class.
- Static methods serve mostly as utility methods or helper methods, since they can't access or modify a class's state.
- Static methods can access and modify the state of a class or an instance of a class.
Ans: #3
Method is just a function object created by def statement.
Method works in exactly the same way as a simple function. But there is one exception: a method's first argument always receives the instance object:
- simple method : defined outside of a class. This function can access class attributes by feeding instance arg.
def outside_foo():
- instance method :
def foo(self,)
- class method : if we need to use class attributes
@classmethod def cfoo(cls,)
- static method : do not have any info about the class
@staticmethod def sfoo()
They have more similarities than differences.
Most of the cases, we can do the equivalent tasks with just a normal function (defined outside of a class). But in terms of software design, clean code, and effectiveness (inner workings), there is a slight difference in their usages.
In this section, we will discuss the difference between the class method and the static method.
There is another one called instance method but inner working of instance method is the same as the class method. Actually, Python automatically maps an instance method call to a class method. For instance, a call like this:
instance.method(args...)
will be automatically converted to a class method function call like this:
class.method(instance, args...)
Suppose we have the following class with instance method foo():
# a.py class A: message = "class message" @classmethod def cfoo(cls): print(cls.message) def foo(self, msg): self.message = msg print(self.message) def __str__(self): return self.message
Because the method foo() is designed to process instances, we normally call the method through instance:
>>> from a import A >>> a = A() >>> a.foo('instance call') instance call
When we call the method, the Python automatically replace the self with the instance object, a, and then the msg gets the string passed at the call which is 'instance call'.
Methods may be called in two ways: the first one through an instance which we did above. Another way of calling is by going through the class name as shown below:
>>> A.foo('class call') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method foo() must be called with A instance as first argument (got str instance instead)
We got the error because Python needs information about the instance. We did not provide anything about the instance. So, we should use the following form when we call a method through a class name as the error message suggested:
>>> A.foo(a, 'class call') class call
Note that we pass the instance a as an argument of the foo() method.
Calls made through the instance and the class have the exact same effect, as long as we pass the same instance object ourselves in the class form.
For a class method, we can just simply call it through class name A:
>>> A.cfoo() class message
Note:
- Bound method (instance call): To call the method we must provide an instance object explicitly as the first argument. In other words, a bound method object is a kind of object that remembers the self instance and the referenced function. So, a bound method may be called as a simple function without an instance later. Python automatically packages the instance with the function in the bound method object, so we don't need to pass an instance to cal the method. In other words, when calling a bound method object, Python provides an instance for us automatically-the instance used to create the bound method object. This means that bound method objects are usually interchangeable with simple function objects, and makes them especially useful for interfaces originally written for functions such as Callback functions.
- Unbound method (class call): Accessing a function attribute of a class by qualifying the class returns an unbound method object:
>>> Callback.changeColor <unbound method Callback.changeColor>
To call the method, we must provide an instance object explicitly as the first argument:>>> obj2 = Callback('purple') >>> t = Callback.changeColor >>> t(obj2) purple
The t is ab unbound method object (a function in 3.0), and we pass in instance.
(Note): In Puython 3.0, the notion of unbound method has been dropped, and what we describe as an unbound method here is treated as a simple function in 3.0. For most purposes, this makes no difference to our code; either way, an instance will be passed to a method's first argument when it's called through an instance.]
>>> class Callback: ... def __init__(self, color): ... self.color = color ... def changeColor(self): ... print(self.color) ... >>> obj = Callback('red') >>> cb = obj.changeColor >>> cb() red
Accessing a function attribute of a class by qualifying an instance returns a bound method object:
>>> obj.changeColor <bound method Callback.changeColor of <__main__.Callback instance at 0x7f95ebe27f80>>
The class method sometime used to define additional constructor. We can check it from cpython/Lib/datetime.py:
... # Additional constructors @classmethod def fromtimestamp(cls, t): "Construct a date from a POSIX timestamp (like time.time())." y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t) return cls(y, m, d) @classmethod def today(cls): "Construct a date from time.time()." t = _time.time() return cls.fromtimestamp(t) ...
Static methods are used when we need to process data associated with classes instead of instances. A static method has no self argument and it is nested in a class and is designed to work on class attributes instead of instance attributes.
Static methods never receive an automatic self argument, whether called through a class or an instance. They usually keep track of information that spans all instances, rather than providing behavior for instances.
There have been some changes in the way how we define static method. Here, I do not want to discuss the variations from one Python version to another version, but just want to show a typical usage sample.
I'll use function decorator(@) for static method, and the syntax looks like this:
class S: @staticmethod def foo(): ...
Internally, the definition has the same effect as the name rebinding:
class S: def foo(): ... foo = staticmethod(foo)
Suppose we have a typical instance counting code like this:
# s.py class S: nInstances = 0 def __init__(self): S.nInstances = S.nInstances + 1 @staticmethod def howManyInstances(): print('Number of instances created: ', S.nInstances)
Then, we make three instances:
>>> from s import S >>> >>> a = S() >>> b = S() >>> c = S()
Now that we have static method, we can call it in two ways:
- call from class
- call from instances
>>> S.howManyInstances() ('Number of instances created: ', 3) >>> a.howManyInstances() ('Number of instances created: ', 3)
Here is a simple code that has all types of methods:
class Methods: def i_method(self,x): print(self,x) def s_method(x): print(x) def c_method(cls,x): print(cls,x) s_method = staticmethod(s_method) c_method = classmethod(c_method) obj = Methods() obj.i_method(1) Methods.i_method(obj, 2) obj.s_method(3) Methods.s_method(4) obj.c_method(5) Methods.c_method(6)
Output:
(<__main__.Methods instance at 0x7f7052d75950>, 1) (<__main__.Methods instance at 0x7f7052d75950>, 2) 3 4 (<class __main__.Methods at 0x7f7052d6d598>, 5) (<class __main__.Methods at 0x7f7052d6d598>, 6)
We get the same output from this code as well:
class Methods: def i_method(self,x): print(self,x) @staticmethod def s_method(x): print(x) @classmethod def c_method(cls,x): print(cls,x) obj = Methods() obj.i_method(1) Methods.i_method(obj, 2) obj.s_method(3) Methods.s_method(4) obj.c_method(5) Methods.c_method(6)
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization