In this week’s ELI5 tutorial I am going to combine my last two ELI5s and talk about special decorators in classes. The first two I will deal with are @classmethod and @staticmethod. These decorators can be very powerful and allow you to make a an object that is very easy for a user to interact with.
@classmethod a.k.a I Want Another __init__
The first one I will deal with is @classmethod, which I personally found to be the most intuitive of these special methods. As the title suggests, this is mainly used to give your objects another way to initialise. As an example, we will use this object that represents a date.
class Date: def __init__(self, year=1970, month=1, day=1): self.year = year self.month = month self.day = day
This is a very simple object, you just initialise it with the current year, month and day. I’ve set it to default to January 1st 1970, which is a special date in computing. However, this may not be the easiest way to create your date object, for example a user might be importing dates in the form of a string and they’d need to break those up before they can create the object. In order to make it more user friendly we can use @classmethod to create a different way to create a date. Here is some sample code.
class Date: def __init__(self, year=1970, month=1, day=1): self.year = year self.month = month self.day = day @classmethod def fromString(cls, dateString="1970-01-01"): pieces = dateString.split('-') return cls(int(pieces), int(pieces), int(pieces))
So, to break down what is doing on here:
On line 7 we have out classmethod decorator.
On line 8 we define our “fromString” method. You’ll see here that it starts with cls instead of self. Like with normal methods, the first argument is special, the convention is to name it cls which is short for class as a way to clearly communicate that it is creating a new instance of the object.
For line 9 I am splitting the string using the dash character. The string I am expecting is the ISO 8601 format, which is objectively the best date format. If you don’t agree fight me on twitter.
Finally, I return a call to cls with the split pieces of the string converted to ints. This will return an instance of the Date object.
So, how does this work in practice? Pretty simple:
- myDate = Date.fromString("2019-02-07") - myDate.day 7
@staticmethod a.k.a Put A Function In My Object
The next special decorator I’ll deal with is @staticmethod. There’s a few things you can use this for, but for this ELI5 I’ll focus on one: making a method that you can use without initialising the object.
For example, lets say our user is using our Date object and just wants to check if a date string is in the correct ISO 8601 format. They don’t want to do anything else with it, so don’t need create an object. While they (or we) could make a separate function for it, since this is so closely linked to our object we can add it into its code with a @staticmethod decorator. Here is how that could look
class Date: def __init__(self, year=1970, month=1, day=1): self.year = year self.month = month self.day = day @staticmethod def isValid(dateString): pieces = dateString.split('-') month = pieces if int(month) > 12: return False return True
This is a partial implementation of code to check if a date is valid. To go through the main points.
On line 7 we have the decorator, as before.
On line 8 we don’t have a self or a cls argument in the function definition. This is because this code does not interact with the object.
The rest of the code splits the string like with the class method and checks if the “month” is bigger than 12. It returns False if it is, otherwise it returns True.
An example of it in action:
- Date.isValid('2019-01-23') True - Date.isValid('2019-13-23') False
As you can see, it returns “True” or “False” depending on the month value and you don’t need to create a Date object first. If you want to practice what you’ve learnt here today, try making an isValid method that can check if the year and day is valid too. For a year it needs to be four digits long while for a day it will vary by month!
You should now have a basic understanding of how to use @classmethod and @staticmethod in your objects. The next ELI5 is going to deal with the other two special object decorators: @property and the setter decorator.