Understanding Object-oriented programming in Python

Are you ready to enter the exciting world of Object-Oriented Programming (OOP)? Well, buckle up and get ready for a thrilling ride!

Object Oriented Programming (or OOP for short) is a programming paradigm that will change the way you think about programming forever. It’s a bit like playing with Legos, but for computer nerds. With OOP, you can create reusable and modular code that is easy to maintain, extend, and share with others.

The concept of OOP is all about creating objects that represent real-world entities, and giving them superpowers (or at least methods). It’s like creating your own army of superheroes, each with their unique abilities and characteristics. With OOP, you can define the properties and methods of an object, and then use them to perform complex tasks with ease. Whether you want to build a game, a web app, or a robotic arm, OOP is the way to go.

Before we move further, I would like to point out OOP is not the only programming paradigm out there. There are tons of other programming paradigms to code in such as;

  • Procedural Programming: Procedural programming is a programming paradigm that focuses on a step-by-step approach to problem-solving. The programs are written as a series of procedures or functions that manipulate data

  • Declarative Programming: This paradigm focuses on describing what a program should do, rather than how it should do it. In this style of programming, programs are written as a set of declarations or rules that specify the desired outcome.

  • Functional Programming: This is a style of programming that emphasizes the use of functions and expressions to solve problems. In this paradigm, functions are treated as first-class citizens, meaning that they can be passed as arguments to other functions and returned as values.

  • And so on.

Another thing to note is that OOP is not language focused, meaning that it can be used in any programming language that supports it. And tons of programming language supports OOP such as Java, Python, PHP, JavaScript etc. But we are going to be focusing on OOP in Python for this article.


We are going to start by discussing the various OOP concepts and how they are implemented in python such as classes, objects, encapsulation, inheritance and polymorphism.

Classes

A class is a blueprint or a template for creating objects of the same type. It defines the attributes and methods that the objects will have. In Python, everything is actually a class (yes, I know it sounds weird), the strings, integers, lists, functions etc are all classes.

Let me prove it to you with the example below;

name = "Paul chase"

age = 24

def fooBar():
    print(name)

print(type(name)) # <class 'str'>
print(type(age)) # <class 'int'>
print(type(fooBar)) # <class 'function'>

From the above, you can see the type of class printed by the side of the print statements. By default, when defining a string variable, it automatically belongs to the str class and inherits all its properties and methods. The same goes for integers, functions, lists etc.

Python also allows us to define our custom classes using the class keyword.

class Animal:
    pass

Class attributes and methods

Class attributes are variables that are defined inside a class and also belong to a particular instance of that class. While methods in simple terms are just functions defined inside a class. Here is an example:

class Animal:
    def __init__(this, color, age):
        # class attributes
        this.color = color
        this.age = age

    # class methods
    def move(this):
        print("I am moving now")

    def talk(this):
        print(f"I am a {this.color} colored animal")

From the above, when the __init__ function is called automatically every time the class is being used to create a new object. In there, we initialize our class attributes.

The this keyword (which is popularly named self in Python programs) is used to reference the current instance of the class. It is also used to access/modify the class attributes and methods.

The methods as I said earlier are just functions defined in the class, but they must contain one parameter which is the first parameter that references the current instance of the class.

Objects

An object is an instance of a class. It has its own unique set of values for the attributes defined in the class. In Python, you create an object by calling the class as if it were a function. Using the Animal Class example above:

# defining an object
dog = Animal('brown', 3)

# accessing object properties
print(dog.color) # brown

# accessing object methods
dog.talk() # I am a brown colored animal

Modifying Object properties: Python also allows us to change the value of object properties and also delete those properties.

dog = Animal('brown', 3)
print(dog.color) # brown

dog.color = 'gray'
print(dog.color) # gray

# deleting of a property
del dog.age
print(dog.age) # will throw an error

Encapsulation

Encapsulation is the practice of hiding the implementation details of a class from the outside world. It is achieved by making the attributes of the class private and providing public methods for accessing and modifying them. For example, suppose we add a eyeColor attribute to our Animal class and we don’t want this attribute to be accessed or modified from outside the class.

class Animal:
    def __init__(this, color, age, eyeColor):
        this.color = color
        this.age = age
        this.__eyeColor = eyeColor # private attribute

    def move(this):
        print("I am moving now")

    def talk(this):
        print(f"I am a {this.color} colored animal")

    # for accessing the eyeColor attribute
    def getEyeColor(this):
        return this.__eyeColor

    # for modifying the eyeColor attribute
    def setEyeColor(this, eyeColor):
          this.__eyeColor = eyeColor

# defining an object
dog = Animal('brown', 3, 'green')

# accessing the private property
print(dog.__eyeColor) # will throw an error
print(dog.getEyeColor()) # green

# modifying the private property
dog.__eyeColor = "yellow" # will throw an error

dog.setEyeColor('yellow')
print(dog.getEyeColor()) # yellow

From the code above, to make the eyeColor attribute private, we added two underscores (__) before the name. Accessing or modifying the eyeColor attribute directly will throw an error, so we created the methods getEyeColor and setEyeColor to access and modify the attribute for us.

Encapsulation is very useful when it comes to hiding sensitive attributes from the outside world. For example, in the case of an Account class where it is logical to keep attributes like balance, transaction PIN etc private and can only be accessed or modified within the class.

Inheritance

This is the practice of creating a new class that is a modified version of an existing class. They are 2 classes in play here, the Parent class (the class that is being inherited from, also called the base class.) and the Child class (the class that inherits from another class, also called the derived class).

The Child class inherits all the attributes and methods of its Parent class and can also add new ones or modify the existing ones. In Python, you can create a subclass by specifying the superclass in parentheses after the subclass name. For example:

class Animal:
    def __init__(this, color, age,):
        this.color = color
        this.age = age

    def move(this):
        print("I am moving now")

    def talk(this):
        print(f"I am a {this.color} colored animal")



class Bird(Animal):
    pass

eagle = Bird('black', 16)
print(eagle. Color) #black

eagle.move() # I am moving now

Using the previous Animal class example, we created a child class called Bird that by default inherits all its parent attributes and methods. So that when we create objects of the Bird class, we can still be able to access the properties and methods of the Animal class.

We can also add custom attributes that belong to the Bird class.

class Bird(Animal):
    def __init__(this, color, age, noOfWings):
        super().__init__(color, age)
        this.noOfWings = noOfWings

eagle = Bird('black', 16, 2)
print(eagle.noOfWings) # 2
print(eagle. Age) # 16

From the code above, we added a new attribute called noOfWings. The super() function there is calling the __init__ function of the parent class and passing the parameters to it. This way we would be to keep the attributes and methods of the parent class.

In addition to adding custom attributes to the Bird class, we can also define custom methods for the Bird class.

class Bird(Animal):
    def __init__(this, color, age, noOfWings):
        super().__init__(color, age)
        this.noOfWings = noOfWings

    def fly(this):
        print("I am flying to the sky")

eagle = Bird('black', 16, 3)

eagle.fly() # I am flying to the sky

Polymorphism

The word polymorphism means having many forms. In programming, polymorphism is the practice of using the same method name to perform different actions depending on the object that is calling it. It is achieved through overriding and overloading.

In Python, overriding is the process of defining a method with the same name in the subclass as in the superclass. Overloading, however, is when a class has multiple methods with the same name but with different number of parameters or types of parameters. Method overloading is not supported in Python ( but there are some tweaks to make it work which is beyond the scope of this article).

Let’s take a look at an example:

class Animal:
    def __init__(this, color, age,):
        this.color = color
        this.age = age

    def move(this):
        print(f"I am moving now")

    def talk(this):
        print(f"I am a {this.color} colored animal")



class Bird(Animal):
    def __init__(this, color, age, noOfWings):
        super().__init__(color, age)
        this.noOfWings = noOfWings

    def fly(this):
        print("I am flying to the sky")

    def move(this, direction):
        print(f"I am moving towards the {direction}")

eagle = Bird('black', 16, 3)

eagle.move('beach') # I am moving towards the beach

As you can see above, we defined a new method move in the Bird class that has the same name as in the Animal class. The move method in the child class now overrides the move method from the parent class. So when the move method is called on the Bird class, it prints out ‘I am moving towards the beach’ instead of ‘I am moving now’.

If for some reason you also want the functionality of the move method from the parent class, you can call the move method with reference to the parent class using the super() function. Here is how;

# In the Bird class
def move(this, direction):
        super().move()
        print(f"I am moving towards the {direction}")

eagle.move('beach') 
# I am moving now
# I am moving towards the beach

Conclusion

If you have made it this far, I would like to say “Well done!”.

Object-Oriented Programming (OOP) is a programming paradigm that offers several benefits for software development. It allows you to model your code around real-world objects and their interactions, making it easier to understand and manage complex code. It promotes code reusability and modular design, making it easier to maintain and extend your codebase over time.

Furthermore, OOP is widely used in modern programming languages such as Python, Java, PHP, Ruby, C++ etc. As such, learning OOP is an essential skill for any aspiring software developer in these modern times. With its well-defined concepts, OOP provides a robust and flexible approach to software development that can help you build better, more reliable applications. Whether you’re building a small application or a large-scale enterprise application, OOP can help you write cleaner, more modular, and more predictable code.

I hope this article has given you a good understanding of Object-Oriented Programming and its benefits. If you have any questions or comments, please feel free to reach out to me on Twitter or LinkedIn. I’d be happy to discuss OOP or any other programming-related topics with you. Thank you for reading.