OOP Basics

The concept of OOP in Python focuses on creating reusable code. This concept is also known as DRY (Don’t Repeat Yourself).

Class

A class is a blueprint for the object.

Example

class Parrot:
    
    # class attribute (same for all instances of a class)
    species = "bird"
    
    # instance attribute (different for every instance of a class)
    def __init__(self, name, age):
        self.name = name
        self.age = age

Object

An object (instance) is an instantiation of a class.

Example:

blu = Parrot("Blu", 10)
woo = Parrot("Woo", 15)

Access class attributes:

>>> blu.species
'bird'
>>> woo.species
'bird'

Access instance attributes:

>>> print(f"{blu.name} is {blu.age} years old.")
Blu is 10 years old.
>>> print(f"{woo.name} is {woo.age} years old.")
Woo is 15 years old.

Methods

  • Functions defined inside the body of a class
  • Define the behaviors of an object

Example

class Parrot:
    
    # class attribute
    species = "bird"
    
    # instance attribute
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sing(self, song):
        print(f"{self.name} is singing {song}.")

    def dance(self):
        print(f"{self.name} is dancing.")
>>> blu = Parrot("Blu", 10)
>>> blu.sing("'Happy'")
Blu is singing 'Happy'.

Inheritance

Inheritance enables us to define a class that takes all the functionality from a parent class and allows us to add more.

class SuperClass:
    
    def __init__(self):
        pass
    
    def super_method(self):
        pass
    
 
class SubClass(SuperClass):
    
    def __init__(self):
        super().__init__()
        pass
    
    def super_method(self):
        """Override method of SuperClass"""
        pass
    
    def sub_method(self):
        """Method that only belongs to SubClass"""
        pass

Example

# parent class
class Bird:
    
    def __init__(self):
        print("Bird is ready")

    def who_is_this(self):
        print("Bird")

    def swim(self):
        print("Swim faster")

# child class
class Penguin(Bird):

    def __init__(self):
        # call super() function
        # run the __init__() method of the parent class inside the child class.
        super().__init__() 
        print("Penguin is ready")

    # overrides method
    def who_is_this(self):
        print("Penguin")

    def run(self):
        print("Run faster")
>>> peggy = Penguin()
Bird is ready
Penguin is ready
>>> peggy.who_is_this()
Penguin
>>> peggy.run()
Run faster

Encapsulation

Encapsulation: Restrict access to methods and variables to prevent data from direct modification.

In python, private attributes is denoted using underscore(s) as the prefix, i.e., _ or __. The main difference between them is:

  • Prefix _ is just a naming convention indicating a name is meant for internal use. It is NOT enforced by the Python interpreter (except in wildcard imports) and meant as a hint to the programmer only.
  • Prefix __ triggers name mangling when used in a class context (i.e., can be not directly accessed) and is enforced by the Python interpreter.

More about underscores see: Underscores in Python.

Example:

class Computer:

    def __init__(self):
        self._min_price = 800
        self.__max_price = 900

    def sell(self):
        print(f"Price: {self._min_price} ~ {self.__max_price}")

    def set_max_price(self, max_price):
        self.__max_price = max_price
>>> computer.sell()
Price: 800 ~ 900

Variable with __ prefix can not be directly accessed or modified:

>>> computer.__max_price
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-20-2eb906d81ddf> in <module>()
----> 1 computer.__max_price

AttributeError: 'Computer' object has no attribute '__max_price'
>>> computer.__max_price = 1000
>>> computer.sell()
Price: 800 ~ 900

We have to use setters and getters:

>>> computer.get_max_price()
900
>>> computer.set_max_price(1000)
computer.sell()

In contrast, variable with _ can be directly accessed or modified:

>>> computer._min_price
800
>>> computer._min_price = 950
>>> computer._min_price
950

Polymorphism

Polymorphism is an ability (in OOP) to use a common interface for multiple forms (data types).

Example:

class Parrot:

    def fly(self):
        print("Parrot can fly")
    
    def swim(self):
        print("Parrot can't swim")

class Penguin:

    def fly(self):
        print("Penguin can't fly")
    
    def swim(self):
        print("Penguin can swim")

# common interface
def flying_test(bird):
    bird.fly()

#instantiate objects
blu = Parrot()
peggy = Penguin()

# passing the object
flying_test(blu)
flying_test(peggy)

Output

Parrot can fly
Penguin can't fly

Reference