How to Do Object-Oriented Programming In Python

When learning Python, you will inevitably come across Object-Oriented Programming or OOP. It is a programming paradigm where we think of the different components in our project as real objects with properties. OOP in Python is implemented by using classes.

If you are just starting with Python make sure to check the previous tutorial in the series to get up to speed.

Setup

There is not much setup for this post, except to have Python installed. If you check my previous post (link above) you can install miniconda to create virtual environments to work. In this case, the code is pretty generic, and anything equal to or above Python 3.6 should work.

Actually, most of the code should work with any Python 3 version, except for the __str__ method since it uses f-strings. F-strings were introduced in Python 3.6.

How to Write a Python Class

Classes allow us to define an object and define what actions it can perform and what attributes can be assigned to it. Moreover, classes are useful when we need to maintain some kind of state or memory between operations. A basic class in Python 3 looks like the following:

class Student(object):       #class definition begins so remember to indent the following code
    def __init__(self,name,year):
        self.name = name
        self.year = year

    def __str__(self):
        #Notice that the '\n' in the text gets printed as a new line
        return f"Student: {self.name}\nSchool Year: {self.year}"
    
    __repr__ = __str__

There are a few things going on here so I’m going to dissect them now. A class definition starts with the keyword class followed by whatever name you want your object to have. In this case, I am defining a class called Student (notice that I capitalized the first letter as this is a convention in Python classes, but not a requirement).

Next, inside the parenthesis, we indicate the parent object for our class. This is how you do inheritance in Python. I will cover object inheritance in a different post. For now, just inherit from object as in the example above since it is the default parent of all Python classes.

From the next line and down, we define the methods that our object has. A method is a function accessible through the class by a dot notation; more on that later. Methods beginning and ending with a double underscore such as __init__, __str__, and __repr__ are special methods that override normal object behavior to suit our needs.

Class Initialization Method: __init__

We use the __init__ method to tell Python how to initialize our object, hence the name. In this method, we indicate what parameters our class needs to start.

We add a special parameter self as the first argument of the function. Self allows the class to reference itself from within. However, self is not a reserved keyword of the language; we could actually choose some other name (but we would need to use it in all other methods of the class consistently) but we write it by convention and you should stick with it.

The other parameters (name and year) are values the class constructor will need in order to create a student object. In order for the student object to remember those values, we also need to store them in internal class variables: self.name, and self.year.

In the real world, a student will probably have some grade assigned to him/her by professors. To represent that, we will add another class variable to our __init__ function. Only that this time I am choosing to start with a fixed grade value instead of defining it at class creation, so we will set it internally to begin with:

def __init__(self,name,year):
    self.name  = name
    self.year  = year 
    self.grade = 0

Defining Custom Class Methods

Besides overriding special class methods like __str__, or __init__ we can also create our own new ones with any name. Continuing with our student example, we will add the ability to decrease or increase grades by the two custom methods below (remember to add self as the first parameter of every method inside the class:

class Student(object):
    ...
    def increase_grade(self, points=1):  #the =1 says that default value is 1 if not provided
        '''Increase student grade by {points} '''
        self.grade += points   # += is a shortcut for self.grade = self.grade + points
    def decrease_grade(self,points=1):
        '''Decrease student grade by {points}'''
        self.grade -= points

We have a function increase_grade which takes a number as a parameter. This number is by how much to increase the student’s grade. By default, the number will be 1, if it is not provided. Inside the function we are simply performing a sum operation on the class variable self.grade. Our other method, decrease_grade, works in a similar fashion except that it subtracts points from the student’s grade.

Let’s Test Our Simple Class

So far we have the following Student class definition:

class Student(object):
    def __init__(self,name,year):
        self.name  = name
        self.year  = year 
        self.grade = 0
    
    def __str__(self):
        #Notice that the '\n' in the text gets printed as a new line
        return f"Student: {self.name}\nSchool Year: {self.year}\nGrade: {self.grade}"

    __repr__ = __str__

    def increase_grade(self, points=1):
        '''Increase student grade by {points} '''
        self.grade += points   # += is a shortcut for self.grade = self.grade + points
    def decrease_grade(self,points=1):
        '''Decrease student grade by {points}'''
        self.grade -= points

The code is simple enough that we can test it directly in the terminal, but you can also use an IDE such as VS Code. Jupyter notebooks would be great for this as well if you know how to use them.

I’ll be using the command line since the code is short enough.

In the terminal, copy and paste the class code above and hit enter. Then, type the following and hit enter but feel free to try other values or names:

student_one = Student(name="James", year="Junior")

We used our class constructor Student to create a Student object whose name is “James” and is a junior at his school and then we referenced it with the variable student_one. Now if you type student_one again in the interpreter and hit enter you should see the following printed on the screen:

Student: James
School Year: Junior
Grade: 0

Keep in mind that we defined what gets printed in the special method __str__ and on the line __repr__ = __str__ from the code.

Access Class Methods With The Dot (.) Notation

Let’s now increase the student’s grade by 1 point by using our increase_grade method defined previously. To access class methods we use dot notation. This means that anything defined inside our class can be accessed through the object by the following syntax:

<object_instance>.<method_name>()

where object_instance is the object we create with a constructor (in our example the object is referenced by student_one). After the object’s name, we put a dot and then the method we want to access (in this case it’s increase_grade). Therefore, to increase the grade by one point we should type in the interpreter and hit enter:

student_one.increase_grade()

After hitting enter, if we type student_one again and press enter to see its contents we should get the following:

Student: James
School Year: Junior
Grade: 1

The grade was increased successfully by one. We can try again but increase the grade by 5 this time:

student_one.increase_grade(points=5)

Once we check the values again we will get:

Student: James
School Year: Junior
Grade: 6

The grade was successfully increased and you can also the object-permanence that classes bring to the table. Our student_one object remembers its grade was 1, and then added 5 to it. Classes keep track of their internal state.

Miscellaneous Info About Classes

An object is also called a class instance. We can create many objects with the same constructor and we can also store them in lists like any other Python type. For instance, we can have a list of students as follows:

highschool_students = [
        Student("Mary Jane","Senior"),
        Student("Carlos","Freshman"),
        Student("George","Junior")
]

Typing the above into the interpreter will create a list containing three students: Mary Jane, Carlos, and George.

Additionally, user-defined objects have their own type. Take a look at what the following built-in function type returns for an integer:

type(10)

the interpreter will print out “int” because 10 is an int and type gives us information about what an object is. You can try it with other objects two. Let’s try it with our student_one object:

type(student_one)

In this case, the interpreter might print out something like “__main__.Student”. Don’t pay attention to the __main__ part for the moment. We can see that our student object is of type Student. Therefore, we have created a new type.

We can even create new classes that inherit from Student instead of the default object, as we did in the beginning. This is useful if we want to extend functionality without adding it to the main class.

this is a diagram of vehicle inheritance model with object-oriented programming
Diagram showing an example of inheritance in object-oriented programming

When to Use Object-Oriented Programming With Python

Classes and object-oriented programming in Python are powerful abstraction concepts that have their time and place. Remember that a class can be useful when you need to model a real-life object or a component with properties that needs to remember its own state.

Try to think of use cases where object-oriented programming makes sense. For example, maybe you want to model different cars so you would define a class Car and make it have attributes such as brand, model, logo, color, plate_number, etc. Then you can create many objects with your Car constructor which will represent actual objects. Maybe you want to model books so you create a Book class; you get the idea.

Additionally, classes can be also used to group related functions into a single structure. In this case, we are not necessarily looking to model an object. It just provides a convenient interface to access those methods and functionality.

Final Thoughts

I tried to keep this introduction to classes fairly short so as to not overload a beginner with too much information. Hopefully, I have shown how object-oriented programming can be used in projects.

I have written other posts on different projects and most of them use classes in one way or another. I recommend you to check them out as well.

Let me know in the comments if you have a particular question or suggestion for future posts. And don’t forget to subscribe to the newsletter. I will be sending notifications when I publish new posts.

Have anything in mind?