DEV Community

Shubham Kumar Gupta
Shubham Kumar Gupta

Posted on

OOP In Python

OOP In Python

  • OOP (Object Oriented Programming) in Python allows us to use objects to represent entities.
  • Each object has its own attributes and methods derived from the Class it belongs to.
  • Some examples of pre-existing classes in Python are Integer, Strings, Lists, Sets, Dictionary, etc.
  • Whenever we are creating, for example, a new list in Python, we are actually creating an object of class List.
  • The new list created has all the methods that are defined in List Class, for example, append, insert, sort, etc.
  • We can create our own classes and objects in Python to build real-life applications
  • Using OOPS allows us to organize, maintain and scale our code more efficiently.
  • The four main pillars of OOP are Encapsulation, Inheritance, Abstraction, and Polymorphism.

Class and Objects

  • An object is to represent an entity belonging to a particular Class.
  • A class is a blueprint for creating objects in OOP.
  • Classes have their own attributes and methods(functions) that can be accessed by objects belonging to that class.
  • We can initialize the attributes of Class using init method (constructor).
class Dog:                          # class naming with PascalCase
    # constructor
    def __init__(self, name, age):
        self.name = name            # creating class attributes
        self.age = age

    def speak(self):                # creating class methods
        return f'My name is {self.name}, and I am {self.age} years old.'

bruno = Dog("Bruno", 9)             # creating object

print(bruno.name)                   # accessing Class attribute from object
print(bruno.age)

print(bruno.speak())                # accessing Class method from object
Enter fullscreen mode Exit fullscreen mode

Output:

  1. Bruno
  2. 9
  3. My name is Bruno, and I am 9 years old.

1. Encapsulation:

  • Encapsulation allows us to hide the object's attributes and method from outside access.
  • Python doesn't have encapsulation in the same sense as other OOP languages.
  • Python doesn't have truly private or protected attributes and methods.
  • Attributes and methods that are meant to be protected are prefixed with a single underscore: _
  • However, they can still be accessed and modified using their names.
  • Attributes and methods that are meant to be private are prefixed with a double underscore: __
  • However, they can still be accessed and modified using name mangling.
  • Also, both can still be accessed and modified using get and set methods.
class Dog:

    def __init__(self, name, age):
        self.__name = name      # private attribute
        self._age = age         # protected attribute

    def get_name(self):         # get method
        return self.__name

    def set_name(self, name):   # set method
        self.__name = name

    def get_age(self):          # get method
        return self._age

    def set_age(self, age):     # set method
        self._age = age

    def __show(self):           # private method
        return f"My name is {self.__name} and I am {self._age} years old."

    def _speak(self):           # protected method
        return "WOOF!"

bruno = Dog("Bruno", 9)

bruno._Dog__name = "Shero"  # modifying private attribute using name mangling
print(bruno._Dog__name)     # accessing private attribute using name mangling

bruno.set_name("Cheems")    # modifying private attribute using set method
print(bruno.get_name())     # accessing private attribute using get method

bruno._age = 6              # modifying protected attribute using its name
print(bruno._age)           # accessing protected attribute using its name

bruno.set_age(3)            # modifying protected attribute using set method
print(bruno.get_age())      # accessing protected attribute using get method

print(bruno._Dog__show())   # accessing private method using name mangling
print(bruno._speak())       # accessing protected method using it's name
Enter fullscreen mode Exit fullscreen mode

Output:

  1. Shero
  2. Cheems
  3. 6
  4. 3
  5. My name is Cheems and I am 3 years old.
  6. WOOF!

2. Inheritance

  • Inheritance allows us to create a new class (called 'child class') based on another class (called 'parent class').
  • The child class inherits all the attributes and methods of the parent class.
  • In addition, the child class it's own unique attributes and methods.
  • There are several types of inheritance namely, single-level, multi-level, multiple, hierarchical, and hybrid.
class Pet:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def show(self):
        print(f'My name is {self.name}, and I am {self.age} years old.')

class Cat(Pet):                     # inheriting parent class in child class

    def speak(self):                # accessing parent class attribute in child class by default
        print("Meow!")

class Dog(Pet):                     # inheriting parent class in child class

    def __init__(self, name, age, sport):
        super().__init__(name, age)     # accessing parent class attribute in child class using super
        self.sport = sport              # child class accessing its own attributes

    def speak(self):
        print(f'My favorite sport is {self.sport}.')

D = Dog("Bruno", 9, 'Football')
C = Cat("Missa", 6)

D.show()                        # accessing parent class method in child class
D.speak()                       # child class accessing its own attributes

C.show()                        # accessing parent class method in child class
C.speak()                       # child class accessing its own attributes

Enter fullscreen mode Exit fullscreen mode

Output:

  1. My name is Bruno, and I am 9 years old.
  2. My favorite sport is Football.
  3. My name is Missa, and I am 6 years old.
  4. Meow!

3. Abstraction

  • Abstraction allows us to hide classes and methods not necessary for the user.
  • We can make an abstract class in python by setting the parent class of any class to ABC after importing it from abc (Abstract Class Bases).
  • After that, we can create an abstract method using the @abstractmethod decorator.
  • We can still access the non-abstract method of abstract classes.
from abc import ABC, abstractmethod

class PET(ABC):         # create an abstract class
    @abstractmethod     # create an abstract method
    def speak(self):
        pass

    def show(self):
        print("I am a Pet.")

class Dog(PET):

    def speak(self):
        print("WOOF!")

class Cat(PET):

    def speak(self):
        print("Meow!")

bruno = Dog()           
bruno.speak()           # ignoring abstract method of abstract class
bruno.show()            # non-abstract method of the abstract class still works

missa = Cat()           
missa.speak()           # ignoring abstract method of abstract class
missa.show()            # non-abstract method of the abstract class still works

nemo = PET()            # will throw an error because we cannot instantiate an abstract class.
Enter fullscreen mode Exit fullscreen mode

Output:

  1. WOOF!
  2. I am a Pet.
  3. Meow!
  4. I am a Pet.
  5. Can't instantiate abstract class PET with abstract method speak

4. Polymorphism

  • Polymorphism allows an object to take on many forms.
  • Polymorphism in Python can be achieved in two ways: method over-riding and duck-typing.

A. Method Over-riding

Method overriding allows a child class to have a different definition of a method already defined in the parent class.

class Pet:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        print(f'My name is {self.name} and I am {self.age} years old.')

class Fish(Pet):
    pass

class Dog(Pet):

    def speak(self):                # over-riding parent's class method
        print(f'WOOF! My name is {self.name} and I am {self.age} years old.')

nemo = Fish("Nemo", 2)
bruno = Dog("Bruno", 9)

nemo.speak()                        # parent's class method runs
bruno.speak()                       # child's class method runs
Enter fullscreen mode Exit fullscreen mode

Output:

  1. My name is Nemo and I am 2 years old.
  2. WOOF! My name is Bruno and I am 9 years old.

B. Duck Typing

Duck Typing allows different types of classes to have the same method name with its own definition.

class Fish:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):                # method of first class
        print(f'My name is {self.name} and I am {self.age} years old.')

class Dog:

    def __init__(self, name, age, breed):
        self.name = name
        self.age = age
        self.breed = breed

    def speak(self):                # method of second class with same name
        print(f'WOOF! My name is {self.name} and I am {self.age} years old, and I am a {self.breed}.')

nemo = Fish("Nemo", 2)
bruno = Dog("Bruno", 9, "Shiba Inu")

nemo.speak()                        # method of first class runs
bruno.speak()                       # method of the second class with the same name runs
Enter fullscreen mode Exit fullscreen mode

Output:

  1. My name is Nemo and I am 2 years old.
  2. WOOF! My name is Bruno and I am 9 years old, and I am a Shiba Inu.

Dunder/Magic Methods:

  • Dunder or magic methods are special methods used in Python classes.
  • They are used to define how objects of classes behave in case they are used with in-built Python operations.
  • Some commonly used dunder methods are init(self,...), str(self), repr(self), len(self), ads(self), del(self), etc.
  • Dunder methods cannot be called, they run automatically when called by in-built python functions.
class SimpleClass:

    # constructor
    def __init__(self, value1, value2):
        self.value1 = value1
        self.value2 = value2

    # string representation of the object
    def __str__(self):
        return f'value 1 is {self.value1} and value 2 is {self.value2}.'

    # string representation for debugging purposes
    def __repr__(self):
        return f'This is an object of the class SimpleClass'

    # returns output when len() function is called
    def __len__(self):
        return 1

    # returns the sum when + operator is called between two objects.
    def __add__(self, other):
        return (self.value1 + other.value1, self.value2 + other.value2)

    def __del__(self):
        print("object deleted")

a = SimpleClass(5, 10)      # __init__ is called
b = SimpleClass(7, 9)       # __init__ is called

print(a)                    # __str__ is called
print(repr(a))              # __repr__ is called
print(len(a))               # __len__ is called
print(a + b)                # __add_ is called
Enter fullscreen mode Exit fullscreen mode

Output:

  1. value 1 is 5 and value 2 is 10.
  2. This is an object of the class SimpleClass
  3. 1
  4. (12, 19)

REFERENCES

Top comments (0)