math.code.life

a python and math learning resource

python

using @staticmethod to write utility classes in python

Introduction

When we start a new project it’s good to think about how it’s going to be structured. We know there will be a hierarchy of files, but we often don’t think about simplifying that hierarchy until it’s too late.

So why not think about using @staticmethod to simplify our hierarchy in areas ahead of time? Some php dude I know, definitely not a friend of mine 😉 , said that this approach is similar to using static methods within a utility class in php.

Note: I use functions inside of modules for the majority of my python coding. However, there have been instances where I experienced convenience by appending a few static methods into an already existing class that does more than just serve a utility purpose. With that out of the way, let’s move on!

Some first steps…

Suppose we want to write some helper functions that return the first, last, and middle (if there is one) characters of a string. We decide to write our functions in a Utility.py file as follows:

def getFirst(string):
    """returns the first element in the string"""
    return string[0]

def getLast(string):
    """returns the last element in the string"""
     return string[-1]

def getMiddle(string, keep='left'):
    """returns the middle, middle-left,
       or middle-right element of a string"""
    if len(string) % 2 == 1:
        return string[len(string) // 2]
    elif len(string) % 2 == 0 and keep == 'right':
        return string[(len(string)+1) // 2]
    elif len(string) % 2 == 0 and keep == 'left':
        return string[(len(string)-1) // 2]

The above code works great, and there certainly isn’t anything wrong with it. We’re using Utility.py to encapsulate our code and it’s as far away as simply saying import Utility.

The question on how to structure your code begins when you get more ambitious. Imagine if you eventually need more Utility functions. You want list, dictionary, and even math utility functions. You could go on creating a bunch of different .py files and pre-pend the corresponding type to the name (i.e. StringUtility.py, ListUtility.py, etc…) but perhaps you don’t want to manage all those files for some valid reason.

So you decide to use a class to contain the above functions as follows:

class StringUtility(object):
    def getFirst(self, string):
        """returns the first element in the string"""
        return string[0]

    def getLast(self, string):
        """returns the last element in the string"""
        return string[-1]

    def getMiddle(self, string, keep='left'):
        """returns the middle, middle-left,
           or middle-right element of a string"""
        if len(string) % 2 == 1:
            return string[len(string) // 2]
        elif len(string) % 2 == 0 and keep == 'right':
            return string[(len(string)+1) // 2]
        elif len(string) % 2 == 0 and keep == 'left':
            return string[(len(string)-1) // 2]

What’s wrong with this solution?

This works great for a minute until you realize that to use these methods you have to do the following:

SU = StringUtility()
first = SU.getFirst('apple')
print(first)
>> a

This will get unwieldy quickly because every time you want to use one of these methods you create an instance object of StringUtility. So how do we get around this? By using @staticmethod. We modify the code as follows:

class StringUtility(object):

    @staticmethod
    def getFirst(string):
        """returns the first element in the string"""
        return string[0]

    @staticmethod
    def getLast(string):
        """returns the last element in the string"""
        return string[-1]

    @staticmethod
    def getMiddle(string, keep='left'):
        """returns the middle, middle-left,
           or middle-right element of a string"""
        if len(string) % 2 == 1:
            return string[len(string) // 2]
        elif len(string) % 2 == 0 and keep == 'right':
            return string[(len(string)+1) // 2]
        elif len(string) % 2 == 0 and keep == 'left':
            return string[(len(string)-1) // 2]

This one little change allows us to use the class itself as a container for the methods, and we can now use them without having to instantiate an instance of said class. We do that as follows:

middle = StringUtility.getMiddle('apple', 'right')
print(middle)
>> p

If you’re familiar with how importing works in python then you can see how similar this is, in fact the code is identical. Instead of importing a .py file we are now just importing a class and calling from there. Lovely!

What about @classmethod?

With @classmethod cls is implicitly passed as the first argument instead of self. You’ll want to use @classmethod when you intend to call a method from a class instead of a class instance. With @staticmethod you can see that neither cls nor self are passed to the method implicitly. They are just a way of grouping class methods that somehow relate to the class with the class.

If you wanted to use @classmethod instead of @staticmethod just modify the above code by adding cls as an argument to the methods and replace @staticmethod with @classmethod instead. However, I would not recommend using @classmethod if the use case is as I described above, since the memory allocation of the program might be affected in a negative way for no reason.

A better use for @classmethod can be found in this stack overflow post.

Conclusion

This is one use for @staticmethod. In general this is equivalent to using a python module filled with functions, however it might be frowned upon by python purists, although in the end it really is a matter of opinion.

In my personal code I overwhelmingly use python modules that contain a bunch of functions rather than utility class methods. With that being said, I still think there are use cases for this technique, especially if someone is reading python code that was written by a php or javascript developer. This will help you understand why their python code might not look like python code.


Bonus Question

  • Under what condition(s) will getMiddle give the same answer as getFirst or getLast?

2 Comments

  1. Cole

    Why have them in a class and not just free functions in a module? You shouldn’t be having static functions in python anyways.
    http://dirtsimple.org/2004/12/python-is-not-java.html

    • atoqed

      Thanks for your comment and question! After reading it I realized that I might not have been clear enough in my post, so I made a few changes to hopefully clarify things. I definitely agree that it’s not pythonic to do it the way I described, BUT, as your link demonstrates, there is certainly python code out there that is not pythonic.

      With that being said, this post was really meant to bring to light how people might be using @staticmethod, even if it’s frowned upon by the python community. At times I have found it useful to tack a static method onto an existing class because it was absolutely related to it and I wanted them grouped from an organizational standpoint. Could I have placed the method as a function just below the class? Sure.

      In general, it’s best to follow the coding conventions of the team you’re working on. If you’re working on a project alone, who cares? Use whatever makes the most sense and call it a day. In the end they are both called by python in literally the exact same anyway.

      More good reading: http://stackoverflow.com/questions/2438473/what-is-the-advantage-of-using-static-methods-in-python

Leave a Reply

Theme by Anders Norén