Object-Oriented Programming

Python

·

4 min read

Introduction

Object-oriented programming (OOP), is a method to structure a code or program by its related properties and behaviours into individual objects.

To do this classes are used. A class is a blueprint for how something should be defined. It doesn’t contain any real data.

Class definitions start with the class keyword, followed by the class's name and a colon :. Any code below the class is part of the class’s body.

class Employee:
    pass

In the above code, the body of the class consists of a single statement: the pass keyword, an empty class. It allows you to run this code without throwing an error.

Instantiate an Object

While the class is the blueprint and instance is an object that is built from the class and contains real data. Creating a new object from a class is called instantiating an object.

emp_1 = Employee()
print(emp_1)
<__main__.Employee object at 0x000001C3C80B6950>

You now have an object 0x000001C3C80B6950 as the memory address, where the object is stored in your computer's memory.

emp_2 = Employee()
print(emp_2)
<__main__.Employee object at 0x000001C3C80B7B20>

Each object of a class has different from one another as you can see above because it’s an entirely new instance and is unique from the first object that you instantiated.

The above class is empty right now, so let’s define some instance attribute that an object should have the same but unique from each other. To keep things simple, we’ll just use first and last names.

Now first you can add attributes to the empty class manually.

emp_1.first = "Ali"
emp_1.last = "Ahmed"

print(emp_1.first)
print(emp_1.last)
Ali
Ahmed

If you want to add some instance attribute for another object you can do this manually for each object.

emp_2.first = "Jhon"
emp_2.last = "Whoo"

print(emp_2.first)
print(emp_2.last)
Jhon
Whoo

However, this isn’t the most Pythonic way of doing that which is not a recommended way. Now you can assign instance attribute to a class by a special method identified by a double underscore at either side of their name, such as __init__.

Python uses special methods to enhance the functionality of classes. Most of them work in the background and are called automatically when needed by the program.

Let’s update the class with an __init__() method that creates .first and .last attributes. You can give __init__() any number of parameters, but the first parameter will always be a variable called self.

When a new object is created, the __init__() method sets the initial state of the object by assigning the values to the object. That is, __init__() initializes each new instance of the class.

class Employee:

    def __init__(self, first, last):
        self.first = first
        self.last = last


emp_1 = Employee("Ali", "Ahmed")

print(emp_1.first) 
print(emp_1.last)
Ali
Ahmed

When a new class instance is created, the instance emp_1 is automatically replaced with the self parameter in __init__() the method, so that attributes can be applied to the object.

As emp_1.first = "Ali" and self.first = first is the same. Now you can create as many objects from your class with less code using the same class structure.

emp_2 = Employee("Jhon", "Whoo")

print(emp_2.first) 
print(emp_2.last)
Jhon
Whoo

Instance Methods

Instance methods are functions/actions that are defined inside a class and can only be called from an instance of that class. If you want to get the full name of a person you can do this manually.

fullName = f"{emp_1.first} {emp_1.last}"
print(fullName)

To do this automatically we use a method (function) in our class.

class Employee:

    def __init__(self, first, last):
        self.first = first
        self.last = last


    def fullname(self):
        return f"{self.first} {self.last}"

emp_1 = Employee("Ali", "Ahmed")
emp_2 = Employee("Jhon", "Whoo")

print(emp_1.fullname())
print(emp_2.fullname())
Ali Ahmed
Jhon Whoo

Now you have the advantage of full code reuse. It's just as easy as replacing one instance with other to create a new instance.

Now one common mistake that, a new programmer can do when creating methods is forgetting the self argument for the instance. If you run this method without self then you can get an error.

class Employee:

    def __init__(self, first, last):
        self.first = first
        self.last = last


    def fullname():
        return f"{self.first} {self.last}"

emp_1 = Employee("Ali", "Ahmed")

print(emp_1.fullname())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [2], line 13
      9         return f"{self.first} {self.last}"
     11 emp_1 = Employee("Ali", "Ahmed")
---> 13 print(emp_1.fullname())

TypeError: Employee.fullname() takes 0 positional arguments but 1 was given

Now, this can be confusing because it doesn't look like we're passing any argument here into fullname() but the instance which in this case emp_1 getting passed automatically. So we have to expect that instance argument in our method, and that's why we added self.