Python and OOP

What's OOP and how to apply it in Python.


What is OOP

OOP stands for Object-Oriented Programming. This term refers to a widely-used programming paradigm.

We can refer to an Object, as a smaller section of the big problem we need to solve.

Between the most common OOP languages, we can find Python, Java, C#, PHP, JavaScript, C++, and many more.

Objects are instances of classes, which contain data (in the form of variables and fields) and methods.

Moreover, Objects' methods can access the class itself, giving them the ability to read and edit the contained data.

For instance, if we defined a program for a Zoo, we'd define Objects for animals, tickets, food, etc.

Most languages support OOP, as it is a powerful way to split programs in smaller and maintainable bits.

On the other hand, in contrast with OOP, we can find procedural programming.

For example, C is merely a procedural programming language. C++ is its evolutions, with OOP capabilities.

OOP in Python

Classes

Python's OOP syntax follows the same rules as any other OOP language. For instance, let's see how to create a class.

In this example, I'm creating a class for a Point Object.

class Point:
    'Defines a 2D Point'

    def __init__(self, x, y):
        self.x = x
        self.y = y

Conventionally, every Python class name must use upper camel case notation.

The string directly underneath the class notation serves as a description for the class' documentation. You can do the same for methods, writing a string just after their initialization.

This description's visible as a hint in any major code editor.

The first method we're defining is the Constructor method. This method uses a special syntax, and it's what's called once we initialize our class (more on this later).

The Constructor's first argument is self, a reserved keyword that grants you access to the Class itself. This is similar to this, if you're familiar with C#, JavaScript, etc..

Inside the Constructor, we're creating two fields containing the x and y coordinates. Note how the self keyword is being used.

We can create additional methods inside the class. For example, we could define a method returning both coordinates in the console.

It's common sense to write methods' names in lowercase and with words divided by underscores.

Let's expand the previous example.

class Point:
    'Defines a 2D Point'

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def get_complete_coordinates(self):
        print("(" + str(self.x) + ", " + str(self.y) + ")")

Class Inheritance

The good thing about OOP is reusability.

For example, let's see how to create a Particle Object class.

class Particle:
    'Defines a Particle'
    gravity = 10
    velocity = {"x": 0, "y": 0}
    acceleration = {"x": 0, "y": 0}

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def get_complete_coordinates(self):
        'Prints the coordinates set'

        print("(" + str(self.x) + ", " + str(self.y) + ")")

    def move(self):
        'Moves the particle around'

        pass

    def add_force(self, force):
        'Adds external forces'

        pass

This is similar to the Point Object. It uses coordinates and can display them in the console.

So why don't we reuse the Point class? We can do it via Inheritance.

For instance, let's import the Point class. You can achieve that by importing Point from the file containing its definition.

Class Inheritance looks like this.

from point import Point

class Particle(Point):
    'Defines a Particle'
    gravity = 10
    velocity = {"x": 0, "y": 0}
    acceleration = {"x": 0, "y": 0}

    def move(self):
        'Moves the particle around'

        pass

    def add_force(self, force):
        'Adds external forces'

        pass

This is a simple example, but taking advantage of Inheritance, you can highly simplify programs and write less redundant code.

Moreover, you may want to override certain methods and implementing a different behavior.

You can do that by simply recreate a method with the same name in the children class.

For example, in here we're overriding get_complete_coordinates():

from point import Point

class Particle(Point):
    'Defines a Particle'
    gravity = 10
    velocity = {"x": 0, "y": 0}
    acceleration = {"x": 0, "y": 0}

    def move(self):
        'Moves the particle around'

        pass

    def add_force(self, force):
        'Adds external forces'

        pass

    def get_complete_coordinates(self):
        'Prints the coordinates set'

        print("Particle position = (" + str(self.x) + ", " + str(self.y) + ")")

Creating instances

Finally, we get to instantiating the classes and creating objects.

To do so, you have to import the class first. Then, you can create a reference like so:

from particle import Particle

particle = Particle(1, 1)
particle.get_complete_coordinates()

This particular code snippet would print "Particle position = (1, 1)" in console.

Conclusion

OOP let us create large and scalable programs, understanding it means taking a big step in code readability and reusability.

This concept is also the same throughout any OOP programming languages, with some minor exceptions.