For example, our
Employee class has two attributes, an
.id and a
.name, but these attributes are initialized with integers and strings, respectively—which is fine, but what’s more interesting is having attributes of some other type that we create. In this video, you are going to create a new class called
Address, which models an address that an employee might want to store in the payroll system so it knows where to send checks to. To start, I’m going to create a new module and I’ll save it as
01:14 What if we just want to get the street address, or the state, or the zip code? With so many possible street addresses, it would be hard to write an algorithm that would parse an address as a string and return just one part of that, especially when you consider that many streets share the same names as cities.
This will take
self, a street, a city, a state, a zip code, and optionally, a second street address called
street2. This equal sign (
=) allows us to assign a default value for this argument, which will be used if the user chooses not to pass in a second street address.
02:14 Basically, I’m saying, “Accept a second street address from the user, but if they don’t supply one, set it equal to an empty string.” Now, I’ll create instance attributes for each of these parameters.
This works, but I think we can take this one step further. Right now, if we ever wanted to print out the entire address of an
Address object, we’d have to concatenate the streets, plus the city, plus the state, plus the zip code.
Let’s create a method in here that will return a nicely formatted string with all the information about the current
Address object. We could call this method whatever we want, and then call it on the object like we would any method—but this presents an opportunity to practice a little bit of inheritance.
If you remember from an earlier video, I said that every custom class we create in Python 3 implicitly inherits from the
object superclass, which itself defines magic methods that Python uses to manage the object. Well, one of those magic methods is called
.__str__(), short for string, which is used to obtain a string representation of the current object. Because our
Address class inherits from
object, it’s got a
.__str__() method too. In fact, let’s see what it spits out. Down here, I’m going to instantiate this class with some random address.
What this means is that if I just pass this object into our
print() function here, the
print() function will print out the string representation of the object, which it will obtain by calling this
.__str__() method under the hood.
I’ll run this and, well, as you can tell, the default string representation of a custom object in Python is pretty boring. We can modify it by providing a new implementation of
.__str__(), effectively overriding the implementation inherited from the
object superclass, which is currently super boring.
Finally, I’ll return the newline character joined by our
lines list. This will return a string where the end of each line—or the end of each element in the list—is separated by a newline character, which pushes the remaining text onto the next line.
Now, I will run this module again and we can see the string representation of the
Address object is displayed in the output panel. All that’s left to do now is modify the rest of the program to use this new
Address class. Before I do that though, I want to delete this object and
print() call down here so it doesn’t start interfering with our main module.
There are a couple of things to notice here. One, notice how I didn’t import the address module. That’s because Python doesn’t require us to assign types to these instance attributes, so this class doesn’t need to know about the
What this means is that we won’t be assigning an
Address when we create the
Employee. Instead, we have to add this
.address attribute after the object has initially been constructed.
The next thing we want to do is modify the
PayrollSystem to print out the employee address when it’s calculating the payroll. I’ll move over to the
hr module and inside the
.calculate_payroll() method, I want to do a check to see if the current employee being processed has an
.address attribute. The code inside here will only be called if the
.address instance attribute is not set to
Now that we finished working on our classes, we can move back into the
program module and give some of our employees an address. Before we can do that, we have to import
contacts so that this module knows about the
I’ll add an
Address object to the
Manager first. Let’s move down to a new line and type
manager.address = contacts.Address(). And then in the parentheses, we need to specify each part of the address as a string.
We’re utilizing the
.__init__() method to construct a new
Address object, and then we assign that object to the
.address attribute after the
Manager has already been created.
This means that we’re not using the
.__init__() method. We’re doing a manual assignment after the fact. What’s good about not using the
.__init__() method is that we don’t have to give every
Employee object an
You’ve now learned how you can create a custom
Address type that can be used by the
Employee class. This
Address class is completely independent of the
Employee, and the
Employee class utilizes
Address objects without any knowledge of them.
Become a Member to join the conversation.