A Complete Example: Putting It All Together
00:00 In the previous lesson, I explained the three different kinds of methods. In this lesson, I’ll show you a complete example using the concepts you’ve learned so far in the course. Time for a quick review.
So far, you’ve learned how to declare class; how to use attributes; how to create instances; how to write instance, static, and class methods; and how to use properties and the descriptor protocol to make methods look like attributes. Before diving into the sample code, I want to go over a couple of concepts in case they’re new to you. First, that
cls named class being passed into a class method?
Whatever it is named, call it with parentheses, and it will return an object. In the previous lesson, I used
* (star) in a method signature to mean any number of arguments. There is a related concept using
** (two stars).
01:15 In this case, a dictionary is passed in, with key-value pairs being treated as the names and values of arguments in the signature. This is a programmatic way of specifying which named arguments you use in a function or method call. Don’t worry if that’s a little muddled.
And I’ve got two more dunder methods for you.
.__str__() gets called when you convert an object to a string, like printing it, and
.__repr__() gets called to represent an object, meaning viewing it in the REPL. By convention,
.__repr__() is supposed to return a string that, if it were pasted into the REPL, would create the same object it is representing. All right, that’s my tangent. Each of these concepts gets used in the example that follows.
02:05 Alright, there’s a little over forty lines of code to go through here, but it’s closer to an actual use case that you might find in the wild. I’m creating a class to represent an employee at a company. As all my employees work for the same place (this must be internal code to the company), I’ve created a class attribute with the company name inside.
And here is the ever-present
.__init__(). To construct my class, I want a name and a date of birth. I’ve learned my lesson, no more first name, last name, just name. The boilerplate inside stores the name and the birth date. But wait, I’ve been tricky. How?
It’s subtly different. Note in
.__init__(), I didn’t set
._birth_date. I set
.birth_date. Well, there is a property and a setter for that, which means the code in
.__init__() is actually using the descriptor protocol.
Let me scroll down a bit so you can see the setter. Here’s the decorator naming the property that is being set,
.birth_date, and the method, whose name isn’t important because it’s wrapped in a decorator, and what it does is set the value.
Here, I’m using the
datetime library to parse a date string and store it as an actual
datetime object. So,
.__init__() takes a string and assigns it to
self.birth_date, which is registered against a setter, which expects a string, converts it to a
datetime object, and then creates
._birth_date, which the property uses. String goes in,
datetime object comes out.
04:15 A better programmer would include some docstrings on all this explaining what values are expected by these methods. If you happen to come across one, send them my way. I have some code that could do with cleaning up.
.compute_age() is an instance method. This function returns the employee’s age based on their birth date and today’s date. First, it uses the
.today() method from the
datetime object to get today’s date. Yep,
datetime is a class even though it isn’t named like one.
04:50 Next, it calculates the difference between today’s year and the year of the employee’s birth. You can’t just do subtraction, though. If it’s June, people born in May are one year older than people born in July, as our birthday is the trigger of incrementing that horrible attribute we all carry known as age. Sorry, old man editorializing. I’m not bitter, though.
05:24 This allows me to compare it to today and check if their birthday has happened this year yet, and if it hasn’t, I’m removing a year and returning that, and otherwise, I return the unmodified value. Okay, time for a class method.
06:29 Each key-value pair in the dictionary gets used here as a named argument to the constructor. In this case, it makes the code a little harder to read, but if you’ve got a lot of arguments and the possibility of defaults that aren’t in the dictionary, this is the only way to go. All right, that’s enough trickiness. Now a little bit of housekeeping.
I’ve put a sentence about our employee. The
.__repr__() method is what gets called when an object is represented in the REPL. Convention is this should be a string that can be pasted in the REPL to create a copy of the object.
07:25 The advantage of that is you can rename the class and this would still work, but I figured I threw enough tricky stuff at you already and just went with the hard-coded value instead. All right, that’s my class.
If instead I print it, it gets converted to a string, calling the
.__str__() method. That prints out the sentence. Remember the sentence calls the
.compute_age() instance method, so lots of code getting run here. When I call
08:37 just a quick reminder that you can get at class variables through the object as well. But you can only read them. Don’t set them. And generally, as I said before, personally I try not to do this. All right, enough with Geralt. Let’s hire a sorceress.
I then pass this dictionary to the
.from_dict() class method, which is a factory. This uses the
** mechanism to map the contents of the dictionary to the arguments in the class constructor and returns us the instance that I’ve stored in
09:32 and printing her out. And there you go: all the class stuff you’ve learned so far in one place. This is actually a fairly realistic example. It’s quite common in software to have objects represent things in the real world—here, an employee of a company.
.compute_age() idea is a little toy-problem-esque, but it represents the idea of doing work on the data belonging to the employee to produce new information, and it’s quite likely you’re going to have something like that in your own code.
Become a Member to join the conversation.