Object-Oriented Programming (OOP) in Python

Python is a multi-paradigm programming language. It supports different programming approaches including structured programming, functional programming and object-oriented programming. In this article, we will focus on object-oriented programming (OOP) in Python.

Object-oriented programming models real-world entities as software objects that have attributes and behaviors. OOP allows for organized code and building maintainable applications. Python fully supports OOP and its key concepts like encapsulation, inheritance, and polymorphism.

Classes and Objects

The basic unit of OOP is a class. A class is like a blueprint for creating objects. We define classes to create user-defined objects that model real-world entities. For example:

# Python class example 

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

  def greet(self):
    print(f"Hello, my name is {self.name}")

p1 = Person("John", 36)
p1.greet() # Hello, my name is John
JavaScript

Here we define a Person class with a constructor to initialize name and age attributes. self refers to the current instance. We also define a method greet() that prints a greeting using the name attribute.

The p1 object is an instance of this class. We can access its attributes and methods directly. OOP allows defining reusable classes to create any number of similar objects.

Inheritance

Inheritance allows a new class to be defined as an extension of an existing class. The new child class inherits attributes and behaviors of the parent class. The child class can define new attributes and override parent class methods.

For example:

class Employee(Person):
  def __init__(self, name, age, id, salary): 
    super().__init__(name, age)  
    self.emp_id = id
    self.salary = salary

  def work(self):
    print(f"{self.name} is working...")

e1 = Employee("Richard", 32, 435, 60000)
e1.work() # calls method on child class 
e1.greet() # calls method from parent class
JavaScript

The Employee class inherits from Person and extends it with id, salary attributes and work() method. Inheritance allows code reuse and modeling intuitive hierarchical relationships.

Encapsulation

Encapsulation refers to binding data and functions into a single unit called class. Class attributes can be accessed directly but private attributes are name-mangled to prevent accidental access. Methods enforce valid access to attributes.

For example:

class BankAccount:
  def __init__(self, balance=0):
    self.__balance = balance

  def deposit(self, amount):
    self.__balance += amount

  def withdraw(self, amount):
    if self.__balance >= amount:
      self.__balance -= amount
    else:
      print("Insufficient funds")

  def get_balance(self):
    return self.__balance
JavaScript

Here the balance attribute is private. The deposit() and withdraw() methods ensure valid balance updates. Encapsulation allows finer control over class attributes.

Polymorphism

Polymorphism refers to syntax allowing different behaviors based on run-time types. In Python, polymorphism is primarily achieved via duck typing – using methods defined by different classes interchangeably.

For example:

class Rectangle:
  def area(self):
    return self.width * self.height

class Circle:
  def area(self): 
    return math.pi * (self.radius ** 2)

def total_area(shapes):
  total = 0
  for shape in shapes:
    total += shape.area()
  return total

shapes = [Rectangle(...), Circle(...)]
print(total_area(shapes))
JavaScript

The total_area() function can compute total area regardless of object type. This polymorphism allows generalizing method calls based on suitable interfaces.

Special Methods

Python classes can implement special methods like:

  • __init__ : Constructor
  • __str__: String representation
  • __add__: Customize + operator
  • __len__: Object length
  • __eq__: Equality check

For example:

class Point:
  def __init__(self, x=0, y=0):
    self.x = x
    self.y = y

  def __str__(self):
    return f"({self.x}, {self.y})"

  def __add__(self, other):
    x = self.x + other.x
    y = self.y + other.y
    return Point(x, y)

p1 = Point(1, 2)
print(p1) # calls __str__
p2 = Point(2, 3) 
p3 = p1 + p2 # calls __add__
JavaScript

Special methods allow customizing common language operations for user-defined classes.

Properties

Python properties provide getter, setter and deleter functionality for class attributes. They allow controlling attribute access without exposing private attributes.

For example:

class Student:
  def __init__(self):
    self._gpa = 0
  
  @property
  def gpa(self):
    return self._gpa

  @gpa.setter
  def gpa(self, value):
    if value < 0 or value > 4:
      raise ValueError("GPA must be between 0 and 4")
    self._gpa = value

student = Student() 
student.gpa = 3.5 # calls setter
print(student.gpa) # calls getter
JavaScript

Here the gpa attribute is private _gpa. The @property decorator exposes it via getter/setter methods.

Class Inheritance Hierarchy

All Python classes inherit from the base object class. Useful built-in hierarchies include:

  • Exception – Base for user-defined exceptions
  • intstrfloat – Numeric types
  • listdicttuple – Collection types
  • file – File handling

We can easily extend these built-in types.

Abstract Base Classes

Abstract base classes define interfaces for their subclasses. Python ABCs provide some common interface methods @abstractmethod that child classes must implement.

For example:

from abc import ABC, abstractmethod

class Vehicle(ABC):

  @abstractmethod
  def num_wheels(self):
    pass

  @abstractmethod
  def start(self):
    pass

class Car(Vehicle):
  # must implement abstract methods
JavaScript

Here Vehicle defines the interface for all vehicles. Concrete classes Car must provide implementations.

Mixins

A mixin class contains methods intended for reuse by multiple child classes. Mixins provide Horizontal code reuse complementing vertical reuse from inheritance.

For example:

class LogMixin:
  def log(self, message): 
    print(f"{self.__class__.__name__}: {message}")
    
class Person(LogMixin):
  def say(self, message):
    self.log(message) 

p = Person()
p.say("Hi there!") # Person: Hi there!
JavaScript

The standalone LogMixin class provides reusable logging functionality easily added to other classes via multiple inheritance.

Conclusion

Python is an effective object-oriented programming language. Key OOP concepts like inheritance, encapsulation, and polymorphism allow for code reuse and abstraction. Special methods customize classes while properties provide control over attribute access. Python OOP features enable the development of reusable and maintainable programs. Understanding OOP principles is important for mastering the Python language.

Leave a Comment