4.2. Classes#

4.2.1. Introduction#

In this section, we will meet classes. Classes are rather like data structures, but they differ in one significant way. They can have functions attached to them. When you create a class in Python, you are essentially creating a special kind of data object that can have functions. Functions embedded within classes are known as methods. You have actually already met methods. In chapter 02_01 when we first learned about strings, you may recall that when we altered the data, we used a “.” after the string object name followed by the function (method) that we wanted to call, e.g. str1.replace(something, something_else). This is what separates a function from a method syntactically in python. While a function is called by itself, a method must be called from a class object. While there is no easy way to explain this distinction, I hope that this explanation will be aided by working with classes and methods at a closer level below.

4.2.2. Creating a Class#

Let’s try creating a basic Class. Our class with store data specifically related to emperors, so let’s go ahead and give it the name Emperor. We expect each emperor in our dataset to have four attributes: name, birth, coronation, and death.

class Emperor:
    def __init__(self, name, birth, coronation, death):
        self.name = name
        self.birth = birth
        self.coronation = coronation
        self.death = death

This is the way this basic class will look. It can be a bit difficult to parse what is happening here when you see it for the first time, so let’s break it down a bit.

In line one, we state:

class Emperor:

The first word we see is the keyword class. Python will see this and expect the syntax for a class object to follow. This is rather like the function’s def which starts to define a new function.

The next thing we see is the class name. In our case, this is Emperor. After the class name, we have (). This is followed by : which concludes our first line. This also means that Python will expect the next line to be indented.

The next line reads:

def __init__(self, name, birth coronation, death)

This is the structure you will use for most classes. The def __init__() is a special method that loads when the class instance is created. Next, we see the parameters the function will take within the (). There are five parameters. The first is self which points to the instance of the class itself. You should always write self here first. The next four parameters are name, birth, coronation, and death.

The self parameter also allows each of the attributes: name, birth, coronation, and death, to be associated with a unique instance of class. This will become more clear as we proceed.

The next four lines are indented and read:

self.name = name
self.birth = birth
self.coronation = coronation
self.death = death

This function will attach these attributes and bind them to the instance of the class, by stating self.name = name and so forth. With this, our class is created. Now, let’s try and make an object that is associated with this class.

As a Carolingianist, I studied Charlemagne, one of the most popular medieval kings who was the second Carolingian king. He was born in 742, coronated as Roman Emperor in 800 and died in 814. These are the four essential pieces of data we need for our class.

We can create our special class object Emperor for Charlemagne by writing the following line of code.

charlemagne = Emperor("Charlemagne", 742, 800, 814)

Notice that we create the class object by using the class name, Emperor() and passing four arguments associated with the four attributes: name, birth, coronation, death.

Let’s now try and print off that class object.

print (charlemagne)
<__main__.Emperor object at 0x7fdd6c16fc70>

This is new and likely not what you expected. This indicates that the object is a special class object. We can get the data from this class object by using the vars() command.

print(vars(charlemagne))
{'name': 'Charlemagne', 'birth': 742, 'coronation': 800, 'death': 814}

We can access specific pieces of data in our class by calling our class object and then using .name_of_attribute. If we wanted to access Charlemagne’s birth year, for example, we could use the following command.

print(charlemagne.birth)
742

At this point, you may be thinking to yourself: “…what’s the big deal? I’ve just made a dictionary.”

And, at this point, you would be right. Our class, the way it is structured is really nothing more than a dictionary of data stored in a special way. This really is not a good usecase for a class. Remember, what makes classes unique is the ability to attach functions to them. Let’s learn how to do that now!

4.2.3. Adding Functions to a Class#

To create a function within a class, we create a function within it. Let’s make a simple function that will print off a string that states who the emperor is and when that emperor was born. To do that, we will alter a class as so:

class Emperor():
    def __init__(self, name, birth, coron, death):
        self.name = name
        self.birth = birth
        self.coron = coron
        self.death = death
    def birth_date(self):
        print (f"{self.name} was born in {self.birth}")

Notice that we have now added a function within our class called birth_date(). This will receive self, or all the data associated with that particular class. This function will now strictly print off an f string that will state when a specific emperor was born.

Now, we just need to create a new class for Charlemagne.

charlemagne = Emperor("Charlemagne", 742, 800, 814)

Within this class, we can access the method birth_date(), like so:

charlemagne.birth_date()
Charlemagne was born in 742

And viola! You have now created your first class that has a specific function associated with it. Although we covered the basics of classes here, it should be noted that we necessarily did not cover everything. Classes have far more complexity and flexibility than presented here; nevertheless, the information provided above should give you enough of a sense about classes to keep moving forward.

Classes and functions are the two building blocks of any project. They allow for you to have tight, neater code that will be easier to read and look polished and professional. They prevent the writing of duplicate code. While it may appear unnecessary at times to view your code through functions and classes, I highly encourage you to start looking at it that way. It will make you a better programmer and you are less likely to have mistakes in your code.