Keeping It Classy

A brief introduction to object oriented programming in Python with a focus on the concept of abstraction

Elliot Macy
4 min readDec 16, 2020
An assembly line of “objects” as they are being instantiated | Photo credit: Carlos Aranda (via Unsplash)

This blog is an introduction to object oriented programming and abstraction in Python. There are many programming languages in addition to Python that, to greater or lesser extents, are object oriented. While the execution here is in Python only, many of the concepts are generalizable.

Object oriented programming (OOP) is a programming paradigm, meaning a way of structuring, or framing, our code. It is an approach to programming geared specifically toward reducing complexity.

Abstraction is OOP’s way of designing and implementing less complicated (but no less functional) code. Abstraction means focusing on what is essential to a given context and stowing any complicated or repetitive procedures out of sight.

In a basic sense, variables allow a value to be accessed repeatedly without having to re-input it explicitly every time. Abstraction just extends this logic to whole code blocks, allowing us to organize our programs succinctly and legibly.

Two cases where we often encounter abstraction are importing Python libraries:

import pandas as pd

… and defining (or calling) functions:

def my_funct(my_funct_parameter):
print(f"You wrote {my_funct_parameter}.")

In both cases, we are using abstractions to stand in for procedures that might be too complex or repetitive to write out explicitly every time we wish to use their functionality.

Python has a number of built in data types, each with specific, predefined functions (known as methods). Much like functions organize procedures into structured groups, methods organize functions into groupings by data type.

The term object simply refers to an instance of a data type, whether instantiated as a variable or a literal value. Below, print() is a function, .upper() is a method specific to the built in str() datatype, and “Hello World” is an instance of the str() data type.

print("Hello World".upper())

Even without realizing it, we are already writing code within the framework of OOP whenever we call a method on an instance of a data type. Of course, having a firm grasp of OOP can be helpful in demystifying how functions relate to methods and why different data types behave differently, but understanding OOP is perhaps most helpful in realizing that we can construct our own, custom data types with unique functionality.

Python uses the term class for defining a data type. Class definitions includes attributes and procedures we wish to bundle together. The syntax looks similar to a function definition, as seen below. By convention, however, class names capitalize the first letter of each word and do not separate words by spaces, whereas function names do not capitalize any letters and separate words by underscores.

class MyClass:
def my_method(self):
print(f"Hi, I am a {self}.")
MyClass.my_method()

Above, MyClass is a class we are defining and my_method is a procedure we are grouping into our class’ functionality. Recall that methods operate like functions except that we call methods on objects, i.e., instances of a specific data type (using the object_name.method_name() syntax). In their definitions, methods take in the object upon which we call them as a parameter. As seen above, by convention we use the name self for this parameter. This is the mechanism by which methods are able to operate on an object.

In addition to structuring our code’s functionality into groups of methods organized by data type, classes also allow us to store multiple values using a single object.

Once we have instantiated an object, we can assign it attributes, which we can later recall, using a similar syntax to calling methods on an object, except that attributes do not use parentheses.

my_object.name = "Obie"
my_object.usefulness = 0

Above we assign our object two attributes and below we access those attributes, passing them to the print() function.

print(my_object.name)
print(my_object.usefulness)

Bringing this all together

Below we can begin to see how class methods and attributes can come together to streamline our code. In the follow example, we define a class with two methods, do_something_cool() and be_more_rad(). The first method checks our object for the attribute .rad and calls the second method if the attribute .rad is falsey. The second method, in turn, sets the .rad attribute to True.

class MyAwesomeClass:    def do_something_cool(self):
if (self.rad):
return "Rad!"
else:
self.be_more_rad()

def be_more_rad(self):
print("This is rad!")
self.rad = True

The first time we call the do_something_cool() method on our object, the .rad attribute will be falsey because we have not yet instantiated it. Therefore, the do_something_cool() method will call the be_more_rad() method that, in turn, will assign our object the attribute .rad = True.

my_first_object.do_something_cool()

Once we call do_something_cool() a second time, .rad will be truthey and so the method will return “Rad!”.

my_first_object.do_something_cool()

As we can see, with OOP, we are able to organize our code concisely and legibly with class definitions keeping complex, repetitious procedures bundled together, apart from our program’s main operations.

--

--

Elliot Macy

“When you measure include the measurer”⠀–MC‏‎‎‎‎ Hammer