Python OOP: Objects, Classes, Abstraction & Encapsulation

Python OOP: Objects, Classes, Abstraction & Encapsulation Interview Questions

What is the difference between a class and an object in Python?
Class:
Blueprint/template for creating objects. Defines attributes and methods.
Object:
Instance of a class. Has actual data and can perform actions.
Example: class Car: (class) vs my_car = Car() (object)
What is __init__ method in Python?
The __init__ method is the constructor in Python. It's automatically called when a new object is created. Used to initialize object attributes:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p1 = Person("Alice", 30) # __init__ is called here
What is encapsulation and how is it implemented in Python?
Encapsulation is bundling data (attributes) and methods that operate on data within a single unit (class). Python implements it using:
  • Public attributes (no underscore)
  • Protected attributes (_attribute) - convention only
  • Private attributes (__attribute) - name mangling
class BankAccount:
    def __init__(self):
        self.balance = 0 # public
        self._min_balance = -100 # protected
        self.__pin = 1234 # private (name mangled to _BankAccount__pin)
What is abstraction in Python OOP?
Abstraction is hiding complex implementation details and showing only essential features. Achieved through:
  • Abstract classes (ABC module)
  • Abstract methods (@abstractmethod)
  • Hiding internal implementation details
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass # Hide implementation details
What is the difference between instance variables and class variables?
Instance Variables:
Unique to each object. Defined using self
Class Variables:
Shared by all instances. Defined inside class but outside methods
class Employee:
    company = "ABC Corp" # Class variable
    
    def __init__(self, name):
        self.name = name # Instance variable
What are getters and setters in Python? How are they different from Java?
Getters and setters are methods to access/modify private attributes. Python uses @property decorator:
class Person:
    def __init__(self, age):
        self._age = age # Protected attribute

    @property
    def age(self): # Getter
        return self._age

    @age.setter
    def age(self, value): # Setter
        if value >= 0:
            self._age = value
In Python, they're accessed like attributes: p.age = 25 (calls setter)
What is the self parameter in Python methods?
self refers to the instance of the class. It's automatically passed when calling instance methods (but not class/static methods). It's a convention, not a keyword.
class MyClass:
    def instance_method(self): # self refers to the object
        return self.value

    @classmethod
    def class_method(cls): # cls refers to the class
        return cls.name
What are class methods and static methods? When to use each?
Class Method (@classmethod):
Receives class (cls) as first parameter. Can modify class state. Uses: factory methods, alternative constructors.
Static Method (@staticmethod):
Doesn't receive self/cls. Can't modify class/instance state. Uses: utility functions related to class.
class Date:
    @classmethod
    def from_string(cls, date_string): # Alternative constructor
        return cls(*map(int, date_string.split('-')))

    @staticmethod
    def is_valid_date(date_string): # Utility
        # Check if date is valid
        return True
What is name mangling in Python and why is it used?
Name mangling is Python's mechanism to make attributes "private" by prefixing with double underscores (__attribute). It's transformed to _ClassName__attribute to prevent accidental overriding in subclasses.
class MyClass:
    def __init__(self):
        self.__private = "secret" # Becomes _MyClass__private

obj = MyClass()
# obj.__private # Error!
# obj._MyClass__private # Works (but shouldn't be accessed)
What is method overriding in Python?
Method overriding allows a subclass to provide a specific implementation of a method already defined in its parent class.
class Animal:
    def sound(self):
        return "Some sound"

class Dog(Animal):
    def sound(self): # Override parent method
        return "Bark"

dog = Dog()
print(dog.sound()) # Output: "Bark"
What are Python's magic/dunder methods? Give examples.
Magic methods (dunder = double underscore) are special methods with __ before and after. Examples:
  • __init__: Constructor
  • __str__: String representation (str())
  • __repr__: Official string representation (repr())
  • __len__: Length (len())
  • __add__: Addition (+)
What is composition vs aggregation in OOP?
Composition:
"Has-a" relationship where child objects cannot exist independently. Strong relationship.
Aggregation:
"Has-a" relationship where child objects can exist independently. Weak relationship.
# Composition: Engine cannot exist without Car
class Engine:
    pass

class Car:
    def __init__(self):
        self.engine = Engine() # Composition

# Aggregation: Professor can exist without Department
class Professor:
    pass

class Department:
    def __init__(self, professor):
        self.professor = professor # Aggregation
What is the difference between __str__ and __repr__?
__str__:
For end-user, readable representation. Called by str() and print()
__repr__:
For developers, unambiguous representation. Called by repr()
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

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

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

p = Point(3, 4)
print(p) # Uses __str__: "Point at (3, 4)"
repr(p) # Uses __repr__: "Point(3, 4)"
What is multiple inheritance and what is the Method Resolution Order (MRO)?
Multiple inheritance: A class inherits from more than one parent class. MRO determines the order in which base classes are searched when looking for a method. Use ClassName.__mro__ or ClassName.mro().
class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):
    pass

d = D()
print(d.method()) # Output: "B" (MRO: D -> B -> C -> A)
print(D.__mro__) # Shows MRO
What is polymorphism in Python?
Polymorphism allows objects of different classes to be treated as objects of a common superclass. Two types:
Compile-time:
Method overloading (not directly supported in Python)
Runtime:
Method overriding, duck typing
# Duck typing example
class Dog:
    def sound(self):
        return "Bark"

class Cat:
    def sound(self):
        return "Meow"

def make_sound(animal): # Accepts any object with sound() method
    print(animal.sound())

make_sound(Dog()) # "Bark"
make_sound(Cat()) # "Meow"
What is the super() function in Python?
super() returns a temporary object of the superclass, used to call parent class methods.
class Parent:
    def __init__(self, name):
        self.name = name

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name) # Call parent's __init__
        self.age = age

child = Child("Alice", 10)
print(child.name) # "Alice"
print(child.age) # 10
What are Python's abstract base classes (ABC)?
ABCs define a blueprint for other classes. Cannot be instantiated. Use abc module:
from abc import ABC, abstractmethod

class Vehicle(ABC):
    @abstractmethod
    def start_engine(self):
        pass

class Car(Vehicle):
    def start_engine(self): # Must implement
        return "Car engine started"

# vehicle = Vehicle() # Error! Cannot instantiate ABC
car = Car() # OK
What is the difference between isinstance() and issubclass()?
isinstance(obj, Class): Checks if obj is instance of Class or its subclass.
issubclass(Class1, Class2): Checks if Class1 is subclass of Class2.
class Animal: pass
class Dog(Animal): pass

dog = Dog()
isinstance(dog, Dog) # True
isinstance(dog, Animal) # True (Dog inherits Animal)
issubclass(Dog, Animal) # True
issubclass(Dog, Dog) # True (class is subclass of itself)
What are descriptors in Python?
Descriptors are objects that implement __get__, __set__, or __delete__ methods. Used to managed attribute access. Property decorator is a descriptor.
class PositiveNumber:
    def __get__(self, obj, objtype=None):
        return obj._value

    def __set__(self, obj, value):
        if value < 0:
            raise ValueError("Must be positive")
        obj._value = value

class MyClass:
    number = PositiveNumber() # Descriptor

obj = MyClass()
obj.number = 10 # OK
# obj.number = -5 # ValueError
What is monkey patching in Python?
Monkey patching is dynamically modifying or extending code at runtime. Can add/modify methods of a class after it's been defined.
class MyClass:
    def original_method(self):
        return "Original"

# Monkey patching
def new_method(self):
    return "Patched"

MyClass.original_method = new_method

obj = MyClass()
print(obj.original_method()) # Output: "Patched"
Warning: Use monkey patching carefully as it can make code hard to understand and debug!
Note: These questions cover fundamental and advanced OOP concepts in Python. Understanding objects, classes, abstraction, and encapsulation is crucial for writing maintainable, reusable code. Remember that Python's OOP implementation differs from languages like Java - it's more flexible with features like duck typing, multiple inheritance, and dynamic modifications.
Python OOP Next