An Introduction to Object Oriented Programming (OOP) in Python

Jake Oddi
4 min readOct 8, 2020

Object Oriented Programming is a type of programming organized around objects containing information and inherent procedures. For example, a Dog object might contain information (called attributes) such as its name, species, and weight and procedures (called methods) such as bark, run, and sleep. OOP helps us be more efficient when programming because it allows us to reuse a portion of our code.

The building blocks of OOP are classes and instances. Classes act as templates for an object, while instances are objects built from those templates but that contain real data. We can define a class like so:

class Horse:
pass

Next, let’s instantiate an object ‘secretariat’ from the ‘horse’ class:

secretariat = Horse()

We can add a class attribute ‘species’, which will have the same value for all instances; the initialization function ‘__init__’, which is used to set the initial state of instance objects by assigning values to the instance attributes; and an instance method ‘make_horse_noise’, which can only be called on instance objects of the Horse class.

class Horse:
species = 'equus'
def __init__(self, color, age)
self.color = color
self.age = age
def make_horse_noise(noise = 'neigh')
return f'The horse says {noise}'

‘self’ must always be the first parameter of __init__, as it is what allows the function to refer to the class attributes. For example, when defining color, we set self.color = color. In doing so, we create an instance attribute called ‘color’ (using self.color) and assign it to the user-given argument for color. Class names follow capital word convention, so when defining a class we write our name like ‘CapitalWordConvention.”

We can access the class’s attributes and methods as follows:

horse = Horse('brown', 3)horse.species
>>> 'equus'
horse.color
>>> 'brown'
horse.make_horse_noise()
>>> 'The horse says neigh'

There are four main concepts associated with OOP: Inheritance, Polymorphism, Encapsulation, and Abstraction.

Inheritance

Inheritance lays the foundation for all of OOP. In the context of the class Horse example, we can create child classes that derive (or inherit) their attributes and methods from the parent Horse class. We can create a child class by adding the name of the parent class in parentheses like so:

class Secretariat(Horse):
pass
secretariat = Secretariat('white', 5)secretariat.age
>>> 5

Without adding any attributes or methods to our definition of the Secretariat class, we can still access its attributes in the same way we would for its parent Horse class. One caveat, however, is that changes made to the parent class will automatically be effected in the child classes. Furthermore, parent attributes and methods can be overridden in the child class by simply rewriting said attribute/method assignment in the child class definition. The OOP class hierarchy bears resemblance to the taxonomic hierarchy for biological classification.

Polymorphism

The concept of polymorphism refers to the shared functionality of multiple different classes. For example, let’s add to our Horse class and define a new class Dog with a shared instance method.

class Horse:
species = 'equus'
def __init__(self, color, age)
self.color = color
self.age = age
def make_horse_noise(self, noise = 'neigh')
return f'The horse says {noise}'
def run(self):
return 'I'm moving fast'

class Dog:
species = 'Canis familiaris'
def run(self):
return 'I'm also moving fast'

In this case, the shared method is run(). We can write a function test_run() that can interact with both a Horse and Dog object and their respective run() methods.

def test_run(animal):
return animal.run()
horse = Horse('brown', 1)
dog = Dog('gold', 2)
test_run(horse)
>>> 'I'm moving fast'
test_run(dog)
>>> 'I'm also moving fast'

Encapsulation

As the name might suggest, encapsulation is used to hide attributes. We employ this technique when we don’t want our attributes to be easily edited. To make an attribute ‘private’ we add an _ or __ before the name.

class Horse:    def __init__(self):
self.__age = 3
def print_age(self):
return f'Horse age: {self.__age}'
def set_age(self, age):
self.__age = age

The only way for us to edit our __age attribute is to do so using an instance method.

horse = Horse()horse.print_age()
>>> 'Horse age: 3'
horse.__age = 4
horse.print_age()
>>> 'Horse age: 3'
horse.set_age(4)
horse.print_age()
>>> 'Horse age: 4'

Abstraction

Abstraction is conceptually very similar to encapsulation. We use it when we want to hide the mechanisms of an instance function from the user as opposed to attributes. It helps keep our code cleaner.

For example, everyday technologies, such as the iPhone, use abstraction. We don’t observe every single process happening inside of the phone. Instead we view the phone as a whole and the internal components and processes are hidden from us. Abstraction in python works the same way, where classes are hidden from the user through class hierarchies.

Summary

Classes and instance objects are the building blocks of OOP. We can define both class and instance attributes, as well as instance methods. The four main features of OOP — Inheritance, Polymorphism, Encapsulation, and Abstraction — are all intertwined as it relates to their functionalities, though all are built on the foundation of inheritance.

--

--