rbrtk rbrtk - 7 days ago 7
Python Question

What's the point of @staticmethod in Python?

I've developed this short test/example code, in order to understand better how static methods work in Python.

class TestClass:
def __init__(self, size):
self.size = size

def instance(self):
print("regular instance method - with 'self'")

@staticmethod
def static():
print("static instance method - with @staticmethod")

def static_class():
print("static class method")


a = TestClass(1000)

a.instance()
a.static()
TestClass.static_class()


This code works correctly, it doesn't return any errors. My questions are:


  1. Do I understand correctly that "self" can be understood as something like "this method will be called from an instance"?

  2. Then again, what's the logic behind @staticmethod - is it to create static methods which can be called from an instance? Isn't that exactly not what static methods are about?

  3. Why would the second approach be favored over the third one? (I assume that since the decorator exists, there is a point to this.) The 3rd option seems to be the simpler and more straightforward.


Answer

Here is a post on static methods. In summary:

  • instance methods: require the instance as the first argument
  • class methods: require the class as the first argument
  • static methods: require neither as the first argument

Regarding your questions:

  1. Yes. While the variable name self is a convention, it pertains to the instance.
  2. Static methods are can be used to group similar utility methods under the same class.
  3. Non-decorated methods without arguments should raise an error. You either need to add self as an argument or decorate the method it with @staticmethod.

It may be more clear to see how these work when called with arguments. A modified example:

class TestClass:

    weight = 200                             # class attr 

    def __init__(self, size):
        self.size = size                     # instance attr

    def instance_mthd(self, val):
        print("Instance method, with 'self':", self.size*val)

    @classmethod
    def class_mthd(cls, val):
        print("Class method, with `cls`:", cls.weight*val)

    @staticmethod
    def static_mthd(val):
        print("Static method, with neither: ", val)

a = TestClass(1000)

a.instance_mthd(2)
# Instance method, with 'self': 2000

TestClass.class_mthd(2)
# Class method, with `cls`: 400

a.static_mthd(2)
# Static method, with neither args:  2

Overall, you can think of each method in terms of access: if you need to access the instance or an instance component (e.g. an instance attribute), use an instance method as it passes in self. Similarly, if you need to access a class, use a class method. If access to neither is important, you can use a static method. Notice in the example above, the same argument is passed for each method type, but access to instance and class attributes differ via self and cls respectively.

If we need to access the class within an instance method, we could use self.__class__:

    ...

    def instance_mthd2(self, val):
        print("Instance method, with `self` but class access:", self.__class__.weight*val)
    ...

a.instance_mthd2(2)
# Instance method, with `self` but class access: 400

I recommend watching Raymond Hettinger's talk Python's Class Development Toolkit, which elucidates the purpose for each method type clearly with examples.