For this ELI5 I’m gonna go over the @property and setter decorators. In my objects tutorial I mentioned that you can access and alter the content of attributes directly, but this is not always ideal. In other words, these allow you to create “getter” and “setter” methods that are common in other programming languages.
@property a.k.a python getter
First we’ll go over the @property decorator, which allows us to define a “getter” method. By a “getter” I mean a method that gets the value of a property. I’ll start by dumping some code down and explain it line by line. This is going to be an object that handles “time” in hours and minutes.
class Time: def __init__(self, hour, minute): self._hour = hour self._minute = minute @property def hour(self): return self._hour @property def minute(self): return self._minute
So, first thing I’ll explain is the init method. You’ll see here that the attributes begin with an underscore (_) character. This is common practice in python to designate that the attribute is “private”, which means it shouldn’t be accessed by users directly. It doesn’t actually restrict anything though, it’s more of a polite suggestion to users.
Now, for the two @property decorated functions. There are very simple getters, all they do is return the values. What @property does specifically though is it allows you to call these methods without including brackets, let me show you what I mean:
- sampleTime = Time(12, 34) - sampleTime.hour 12
As you can see, I was able to get the _hour value by typing .hour. You might be wondering why you would bother using this though – it isn’t actually doing anything differently than if we just use self.hour and self.minute in the init right? Well, the main answer is that it’ll gives you access to setters.
A setter is simply a method for setting the value of an attribute. These are incredibly useful as it allows you to introduce sanity checking and other additional steps when a user changes an attribute in the object. Here is some sample code showing a setter for the minute attribute:
class Time: def __init__(self, hour, minute): self._hour = hour self.minute = minute @property def hour(self): return self._hour @property def minute(self): return self._minute @minute.setter def minute(self, value): if value < 60: self._minute = value else: raise ValueError
So, let’s jump right down to line 14. Here I name my method @minute.setter, this only works if there is a method named “minute” decorated by an @property already. This code will then check if the value the user is entering is less than 60 and if it isn’t it will throw a ValueError. This means that if a user tries to set the minute of the time to an invalid value it won’t let them.
Update: You’ll also notice that I have self.minute in the init instead of self._minute, this means that the init will also use the setter and throw an error when it’s too big. Thanks to Dean for suggesting this addition!
So, there you have it, a quick intro to @property and how you can use it to create a setter for your python objects. if you want to practice, try adding a getter and setter for “second” and a setter for “hour” in the code above.
The next ELI5 (and final for a while) will be on subclassing. Stay tuned!