An intuitive approach to understanding Classes in Python

I recently dived a bit into Classes in Python. During my journey, there were a lot of words such as objects, attributes, methods, functions, class, etc. thrown around loosely and that were, at times, used interchangeably - when this should not have been the case. I also thought that it would be nice to pen down my understandings into a post so that it might help others. This post will just focus on what exactly classes in Python are and would not be addressing the "object-oriented" part that usually come with it. The idea is just to give a feel and keep things simple. I might try to cover other aspects in a later post. So let's just get straight into it.

So what is a class in Python?

Before we directly answer this, I feel we should understand what an object in Python is and then try build things upon it.

Objects: Python being an object-oriented programming language, everything inside it is technically an object. So all the variables, lists, dictionaries, sets, functions, etc. are objects in Python. And each object has a class.

To make things easy to grasp and correlate later, let's take an example. If you run the commands shown in the image below in your iPython console, you will see an integer 'a' assigned the value of 3 belongs to the class int.

image.png

So to conclude the above example, 'a' is our object which belong to the class int. Similarly, functions belong class function, strings belong class str and so on.

Now let's build upon this knowledge to guess what a class might be in Python? Simply put, a class is a way to create objects that facilitate our tasks while coding. Make sense, right? In the above example, we took the help of a pre-defined class called as int to create our object 'a'.

Python allows us to create our own classes that we can leverage in our programming tasks. We define our own classes using the keyword class, and it can contain attributes (fancy word for data/variables) and/or methods (fancy words for functions defined inside a class).

Why create a class in Python?

Let's say you own a footwear shop, and you want to manage its inventory via code. In other words, you want to track what footwear you have in your shop, its type, color, size and if possible, price. You have two options:

  • Create a variable for each item and store its value.
footwear1_type = 'sandals'
footwear1_color = 'blue'
footwear1_size = '7'

footwear2_type = 'sneakers'
footwear2_color = 'red'
footwear2_size = '9'

Clearly this not feasible if your inventory is large enough and have to make frequent changes or addition. Imagine the number of variable that you would have to create with thousands of items in your shop. We now move on to second and more practical option.

  • Create a class to define the blueprint so that you can directly create objects using this blueprint. (please use this code snippet just to see how we define a class and use it to create objects as per our needs and ignore the self and __init__ for now)
"""
# Creating a blueprint
A footwear in your shop. Each footwear has following characteristics:

  Attributes:
  type: a string representing the type of footwear
  color:  a string representing the color of footwear
  size: an interger representing the size of footwear
  price: an optional integer representing the price of footwear in euros
"""
class Footwear(object):
  def __init__(self, type, color, size, price=None):
    self.type = type
    self.color = color
    self.size = size

"""
# Using the blueprint to create objects
"""

bluesandals = Footwear("sandals", "blue", 7)
redsneakers = Footwear("tsneakers", "red", 9, 100)

Please note that class Footwear(object): does not create any footwear entry. To create an entry (aka an object) we call the class's __init__ method (function) with the proper number of arguments (minus self, which we'll get back soon here). And this exactly what we have done when we created bluesandals and redsneakers. Now you could make this more complex if you want by adding more functions such as a function to get the price, set the price if already not set during variable creation. Remember the price was optional as the default is set to None in the __init__ method (function). Let's try one more:

def set_price(self, price):
        """Set the price of an item of footwear."""
        self.price = price
        print(f"Hey there! I am setting the price of the {self.color} {self.type} to ${price}.")

Since in the above example, we did not set the price of bluesandal. Let us use this method (function) to set the price. We do it in the following way.

bluesandals.set_price(500)

We will then get the following message on our console due to the print statement.

Hey there! I am setting the price of the blue sandal to 500.

Let us now address the devils in the room. The infamous self and __init__.

What is self?

self is a parameter to all the Footwear methods. Put another way, a method like set_price defines the instructions for setting the price of some abstract footwear entry. Calling bluesandals.set_price(500) puts those instructions to use on the bluesandals instance.

So in other words, bluesandals.set_price(500) is just another form for Footwear.set_price(bluesandals, 500), which by the way Python will understand too.

What is __init__?

The __init__ method (function) is called every time an object is created using our blueprint aka class. The __init__ method lets the class initialize the object's attributes(in our cases the type, color, size and price) and serves no other purpose. It is exclusively used within classes.

Just imagine that bluesandals = Footwear("sandals", "blue", 7) is the same as calling bluesandals = Footwear(bluesandals , "sandals", "blue", 7). In our case, we call bluesandals as a fully-initialized object.

That's all for now from my side. For any suggestions or comments, my Twitter DMs are always open. See y'all!