Python is a unique language in that it is fairly easy to learn, given its straight-forward syntax, yet still extremely powerful. There are a lot more features under the hood than you might realize. While I could be referring to quite a few different things with this statement, in this case I'm talking about the decorators
@staticmethod. For many of your projects, you probably didn't need or encounter these features, but you may find that they come in handy quite a bit more than you'd expect. It's not as obvious how to create Python static methods, which is where these two decorators come in.
In this article I'll be telling you what each of these decorators do, their differences, and some examples of each.
The @classmethod Decorator
This decorator exists so you can create class methods that are passed the actual class object within the function call, much like
self is passed to any other ordinary instance method in a class.
In those instance methods, the
self argument is the class instance object itself, which can then be used to act on instance data.
@classmethod methods also have a mandatory first argument, but this argument isn't a class instance, it's actually the uninstantiated class itself. So, while a typical class method might look like this:
class Student(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name scott = Student('Scott', 'Robinson')
@classmethod method would be used like this instead:
class Student(object): def from_string(cls, name_str): first_name, last_name = map(str, name_str.split(' ')) student = cls(first_name, last_name) return student scott = Student.from_string('Scott Robinson')
This follows the static factory pattern very well, encapsulating the parsing logic inside of the method itself.
The above example is a very simple one, but you can imagine more complicated examples that make this more attractive. Imagine if a
Student object could be serialized in to many different formats. You could use this same strategy to parse them all:
class Student(object): def from_string(cls, name_str): first_name, last_name = map(str, name_str.split(' ')) student = cls(first_name, last_name) return student def from_json(cls, json_obj): # parse json... return student def from_pickle(cls, pickle_file): # load pickle file... return student
The decorator becomes even more useful when you realize its usefulness in sub-classes. Since the class object is given to you within the method, you can still use the same
@classmethod for sub-classes as well.
The @staticmethod Decorator
@staticmethod decorator is similar to
@classmethod in that it can be called from an uninstantiated class object, although in this case there is no
cls parameter passed to its method. So an example might look like this:
class Student(object): def is_full_name(name_str): names = name_str.split(' ') return len(names) > 1 Student.is_full_name('Scott Robinson') # True Student.is_full_name('Scott') # False
self object is passed either, that means we also don't have access to any instance data, and thus this method can not be called on an instantiated object either.
These types of methods aren't typically meant to create/instantiate objects, but they may contain some type of logic pertaining to the class itself, like a helper or utility method.
@classmethod vs @staticmethod
The most obvious thing between these decorators is their ability to create static methods within a class. These types of methods can be called on uninstantiated class objects, much like classes using the
static keyword in Java.
There is really only one difference between these two method decorators, but it's a major one. You probably noticed in the sections above that
@classmethod methods have a
cls parameter sent to their methods, while
@staticmethod methods do not.
cls parameter is the class object we talked about, which allows
@classmethod methods to easily instantiate the class, regardless of any inheritance going on. The lack of this
cls parameter in
@staticmethod methods make them true static methods in the traditional sense. They're main purpose is to contain logic pertaining to the class, but that logic should not have any need for specific class instance data.
A Longer Example
Now let's see another example where we use both types together in the same class:
# static.py class ClassGrades: def __init__(self, grades): self.grades = grades def from_csv(cls, grade_csv_str): grades = map(int, grade_csv_str.split(', ')) cls.validate(grades) return cls(grades) def validate(grades): for g in grades: if g < 0 or g > 100: raise Exception() try: # Try out some valid grades class_grades_valid = ClassGrades.from_csv('90, 80, 85, 94, 70') print 'Got grades:', class_grades_valid.grades # Should fail with invalid grades class_grades_invalid = ClassGrades.from_csv('92, -15, 99, 101, 77, 65, 100') print class_grades_invalid.grades except: print 'Invalid!'
$ python static.py Got grades: [90, 80, 85, 94, 70] Invalid!
Notice how the static methods can even work together with
validate using the
cls object. Running the code above should print out an array of valid grades, and then fail on the second attempt, thus printing out "Invalid!".
In this article you saw how both the
@staticmethod decorators work in Python, some examples of each in action, and how they differ from each other. Hopefully now you can apply them to your own projects and use them to continue to improve the quality and organization of your own code.
Have you ever used these decorators before, and if so, how? Let us know in the comments!