Introduction to Python Classes

This article introduces classes in Python, explaining what they are, their components, and why we use them. We’ll also look at some unique characteristics of classes to assist us in creating cleaner code.

When I first began coding, the subject of classes in Python was a complete mystery to me. The explanations and jargon employed made understanding more difficult, not more accessible, and the examples used involved barking dogs, speeding cars, and people’s age and hair colour. It made it difficult to understand why and when I’d use a class. In this article, I will give you enough to understand the basics of classes, and we’ll use some real-world examples to illustrate the concepts discussed.

This is a two-part series. You can read the second part of this Python introduction here:

*** An Introduction To Python Classes – Inheritance, Encapsulation, and Polymorphism ***

Object-Oriented Programming (OOP) versus Procedural Programming

If you’re like me, you began your coding in a very procedural manner. You broke the problem you wish to solve down into smaller steps, possibly used a basket full of functions to solve individual parts of those steps, passed each step the different data that they needed to operate, then finally stitched everything together at the end to produce a result. It works, but it’s not that efficient and not very readable for those coming behind.

Using object-oriented programming, we keep the code and data together and treat the problem as a series of objects.

Objects Explained

In real life, an object has properties specific to that object; it also has behaviours or functions it can perform. Take a cardboard carton; it has a name, size, weight, material, and colour. It also has a function of folding flat and be one size or be assembled, and be another.

Python objects are no different.  In Python, the properties are called attributes, and the behaviours are called methods.

How Do Classes Feature In Objects?

Classes are simply an outline or blueprint that defines the attributes and methods possessed by objects created using that class. In the real world, scopes of work or house plans do the same thing. Let’s look at a coding example to try and explain this. We will assume we have a small grocery store and wish to write a stock management programme. Here’s the first piece of code.

class Stock:

    category = 'Grocery Item'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

When you define a class, it is similar to writing a function; you use def keyword, followed by the name of the class. From the first line of code, we are defining a class called Stock. As with functions, you need to add a colon at the end of the definition. The following lines will be indented.

The following line defines a variable called category, and I’ve passed the value of 'Grocery Items' as a string. The variable category is called a class variable. Unless specifically changed, all objects created under the class Stock will have the category 'Grocery Items‘.

💡 Note: In Python, for some strange reason, instead of using the word ‘create’, we use the word ‘instantiate’. Instantiate means ‘to provide an instance of something’. An instance is ‘a case of something occurring. Therefore while ordinary people would talk of creating an object from the class’ Stock’, Python coders talk of ‘instantiating an instance of the class Stock’. Weird huh? Use whatever words you want to make sense of it, but you’ll understand when people use the terms from now on.

Then you see the following code:

def __init__(self, stock_code, description, buy_price, mark_up):

As this is indented, you’ll understand that this definition sits within the class Stock. It defines what is called class attributes. Much like our cardboard box example earlier, class attributes define the specific characteristics of objects created under this class.

In this example, every object created under this class will have the characteristics, stock_code, description, buy_price, and mark_up.  The critical thing to remember is that the values of these characteristics will be different for each object. We will create those values when we define each object. You’ll see that soon.

What is the __init__ and self syntax used in a class?

To understand more than you ever needed to know about the __init__ function, see this article.  In brief, __init__() is a python function that initialises, or sets, the attributes you wish your objects to possess.
The term self is simply a placeholder for the object you are creating from that class, and it then allows you to access variables from that class. You don’t have to use self, you can put in any name you like; it’s just that self is an accepted usage.

The other items after self in the __init__  function are the parameters you will need to pass to the class when you wish to create an object.  In this example, they are stock_code, description, buy_price, and mark_up. If you do not supply these parameters when creating an object from this class, you will get a TypeError.

Setting class attributes

self.code = stock_code
self.desc = description
self.buy = buy_price
self.margin = mark_up

These statements within the initialisation function create new attributes called code, desc, buy, and margin and pass them the parameters supplied when creating the object.

So we are done creating our class for the moment. We’ll discuss other things we can do within the class shortly, but first, let’s create an object.

Creating our first object from the newly defined class

To create an object from a class, you provide a name for the object and pass to it the class and parameters applicable to the object you wish to create. In our example, I will name a stock item with its stock code and then pass the class and the parameters required by the class to that name. Here’s the code.

class Stock:

    category = 'Grocery Item'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

# Create, or 'instantiate' an object of class Stock
C298 = Stock('C298', 'Chicken Soup', 0.75, 1.553)

With that last line of code, we’ve given the name C298 to the object we wish to create. Then we’ve called the class Stock and passed in the correct order the specific values of the parameters we created under __init__. We ignore the self parameter as that will now automatically become the objects name. When we run this code, a new object, C298, will be created. The attribute values will be accessible as part of that object. Let’s check that.

print(C298.category)
print(C298.desc)
print(C298.buy)
print(C298.margin)
print(C298.code, '\n')

print('In the {} category we have {} at a cost price of ${}.'.format(C298.category, C298.desc, C298.buy))

# Result

Grocery Item
Chicken Soup
0.75
1.553
C298 

In the Grocery Item category we have Chicken Soup at a cost price of $0.75.

The first print command calls for the class variable ‘category’ and returns’ Grocery Item’. The remaining commands ask for each attribute value that was provided when we created the object.

Using Methods In A Class Definition

Earlier, we explained that objects have properties called attributes in Python, and behaviours are called methods. We’ve seen how we create attributes and pass values to them. Now we’ll discuss methods.

I think of methods as being like the functions we use in everyday coding, except that we call them methods when they’re inside a class. We define them in precisely the same way; the benefit of using them in a class is that they have access to, and can alter, the attributes and variables within that class.

Like functions, methods may not require arguments, or they may need a number of them. Let’s look at both.

class Stock:

    category = 'Groceries'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

    def sell_price(self):
        print('Retail price = $', round(self.buy * self.margin, 2))

    def sale(self, discount):
        print('The discounted price of {} is $'.format(C298.desc), round(self.buy * self.margin * (1-discount), 2))

C298 = Stock('C298', 'Chicken Soup', 0.75, 1.553)

C298.sell_price()

C298.sale(.15)

In this example, I’ve defined two methods. The first, sell_price(), creates a method that calculates a selling price for the object using the purchase price and multiplying it by the margin. This method does not have any arguments, and therefore no parameters need to be passed when called, hence the C298.sell_price( ) syntax.

The second method, sale( ), calculates a sales price based on a discount figure. Therefore it stipulates an argument called ‘discount’. When we call the method, we must give a discount figure or get an error. With this code, I’ve passed it a discount of 15%. Let’s run the code.

# Result

Retail price = $ 1.16
The discounted price of Chicken Soup is $ 0.99

We can create as many grocery objects as we wish using the class Stock; each object will be unique but possess the corresponding characteristics of the class.

Hopefully, with this example, you can see the value of using classes. They allow us to adhere to the DRY principle in Python; Don’t Repeat Yourself. We need to call the class and pass the values, and we have a new object without a large amount of repetitive coding.

Summary

In this article, we introduced the basics of classes in Python. We discussed forming a blueprint outlining the variables, attributes and methods to apply to any object we create using that class.

We created a Stock class and defined the variables and attributes we wished it to apply to any objects we create. We then created an object using that class and applying the specific values for the class attributes. We then interrogated the object to see that it did contain those values.

Finally, we introduced methods that can access, use, and modify the attributes within the class. We created a method to calculate the selling price of the grocery items and a second method to calculate a sales price using a supplied discount.

In part two of this article, we’ll build on this example to discuss inheritance, encapsulation, and polymorphism:

*** An Introduction To Python Classes – Inheritance, Encapsulation, and Polymorphism ***