Interview Prep — OOP Track

OOP Interview Questions

15 OOP questions — the four pillars, SOLID principles, design patterns, and Python-specific OOP features. Tagged for FAANG and startups.

PillarsSOLIDPatternsPython OOP
15Total
4Pillars
5SOLID
3Patterns
3Python OOP
Progress saved locally. Check off questions — saves to your browser. No account needed.
0
/ 15 Done
01 What are the four pillars of OOP? OOP
  • Encapsulation: Bundling data and methods together; hiding internal state. Achieved via private/protected attributes in Python (_name, __name).
  • Abstraction: Exposing only essential features, hiding complexity. Abstract classes/interfaces define contracts.
  • Inheritance: A child class inherits attributes and methods from a parent class, enabling code reuse.
  • Polymorphism: Same interface, different behaviour. Method overriding (runtime) and method overloading (compile-time — not natively in Python).
05 What is polymorphism? Explain with a Python example. OOP
Polymorphism means "many forms" — objects of different classes can be treated through a common interface.
python
class Dog:
    def speak(self): return "Woof!"

class Cat:
    def speak(self): return "Meow!"

def make_sound(animal):
    print(animal.speak())

make_sound(Dog())  # Woof!
make_sound(Cat())  # Meow!
Types: Compile-time (overloading), Runtime (overriding), Duck typing.
06 What is encapsulation? How is it achieved in Python? OOP
Encapsulation bundles data and methods together while restricting direct access to internal state.

Python access conventions:
  • public — normal attribute (accessible everywhere)
  • _protected — convention, "please don't touch" (still accessible)
  • __private — name-mangled, harder to access (_ClassName__attr)
python
class BankAccount:
    def __init__(self):
        self.balance = 0       # public
        self._pin = "1234"     # protected
        self.__secret = "xyz"  # private

    def get_balance(self):
        return self.balance
07 What is abstraction? How is it different from encapsulation? OOP
Abstraction hides complex implementation details and shows only essential features.
Encapsulation hides internal state and requires interaction through methods.

Key difference: Abstraction = hiding complexity (design level). Encapsulation = hiding data (implementation level).
python
from abc import ABC, abstractmethod

class Payment(ABC):
    @abstractmethod
    def process_payment(self, amount):
        pass

class CreditCard(Payment):
    def process_payment(self, amount):
        print(f"Processing ${amount} via Credit Card")
02 Explain inheritance types in Python with examples. OOP
  • Single: class B(A)
  • Multiple: class C(A, B) — Python uses MRO (Method Resolution Order) / C3 linearization
  • Multilevel: A → B → C (chain of inheritance)
  • Hierarchical: Multiple children from one parent
  • Hybrid: Combination of the above types
MRO: Use ClassName.__mro__ or help(ClassName) to inspect the resolution order in multiple inheritance.
03 What is the difference between @classmethod, @staticmethod, and instance method? OOP
python
class MyClass:
    count = 0

    def instance_method(self):   # access self & class
        return self

    @classmethod
    def class_method(cls):        # access class, not instance
        return cls.count

    @staticmethod
    def static_method(x):        # no self or cls
        return x * 2
Method typeFirst argUse case
Instance methodselfAccess/modify instance state
Class methodclsFactory methods, alternative constructors
Static methodUtility functions logically belonging to the class
04 What are dunder (magic) methods in Python? OOP
Dunder methods (double underscore) let you define how objects behave with built-in operators and functions.
  • __init__ — constructor
  • __str__, __repr__ — string representation
  • __len__len(obj)
  • __eq__, __lt__, __gt__ — comparison operators
  • __add__, __mul__ — arithmetic operators
  • __getitem__, __setitem__ — indexing obj[key]
  • __iter__, __next__ — make object iterable
  • __enter__, __exit__ — context manager protocol
python
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

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

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

v = Vector(1, 2) + Vector(3, 4)  # Vector(4, 6)
08 What is the difference between abstract class and interface in Python? OOP
AspectAbstract ClassInterface (Python pattern)
Concrete methodsAllowedNot allowed (all abstract)
StateCan have instance variablesTypically no state
Multiple inheritPossible but complexEncouraged
Python syntaxABC + mix of methodsABC with all @abstractmethod
Note: Python prefers "duck typing" over strict interfaces — "if it walks like a duck and quacks like a duck, it's a duck."
11 What are Python decorators in the context of classes? OOP
Common class decorators:
  • @property — getter method that looks like an attribute
  • @x.setter — setter with validation
  • @classmethod — receives class as first argument
  • @staticmethod — no self or cls
python
class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name.title()

    @name.setter
    def name(self, value):
        if not value: raise ValueError("Name required")
        self._name = value

p = Person("alice")
print(p.name)   # Alice  (via @property getter)
p.name = "bob" # calls @name.setter
13 What is __slots__ and when should you use it? OOP
__slots__ restricts allowed attributes and saves memory by preventing automatic __dict__ creation per instance.
python
class Point:
    __slots__ = ['x', 'y']

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

# p.z = 1  → AttributeError: 'Point' object has no attribute 'z'
Benefits:
  • 40–50% memory reduction when creating many instances
  • Faster attribute access
Drawback: Cannot add dynamic attributes after class definition.
09 What is the Diamond Problem in inheritance? How does Python solve it? OOP
The Diamond Problem occurs when a class inherits from two classes that both inherit from a common base — creating ambiguity about which path to follow.
text
      A
     / \
    B   C
     \ /
      D      ← Which A.__init__ does D call?
python
class A:
    def hello(self): print("A")

class B(A):
    def hello(self): print("B"); super().hello()

class C(A):
    def hello(self): print("C"); super().hello()

class D(B, C): pass

D().hello()        # B → C → A  (MRO order)
print(D.__mro__)   # (D, B, C, A, object)
Python's solution: C3 Linearization creates a consistent, deterministic MRO.
Rule of thumb: Always use super() in cooperative multiple inheritance so the MRO chain is properly followed.
10 What is composition? When should you prefer it over inheritance? OOP
Composition = "has-a" relationship (object contains other objects).
Inheritance = "is-a" relationship (child is a specialised parent).

python
# Inheritance: Car IS-A Vehicle
class Car(Vehicle): ...

# Composition: Car HAS-A Engine
class Engine:
    def start(self): print("Engine started")

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

    def start(self):
        self.engine.start()
Prefer composition when:
  • You need to change behaviour at runtime (swap out the component)
  • Inheritance would create tight coupling
  • Multiple inheritance becomes complex or fragile
Principle: "Favor composition over inheritance" (Gang of Four) — more flexible, easier to test.
12 Explain the Singleton pattern and how to implement it in Python. OOP
Singleton ensures a class has only one instance and provides global access to it.
python
# Method 1: __new__
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance

# Method 2: via Metaclass
class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

# Verify
a, b = Singleton(), Singleton()
print(a is b)  # True
Use cases: Database connections, logging, configuration managers.
Caution: Singletons can make testing harder — consider dependency injection as an alternative.
14 What is the difference between shallow copy and deep copy in OOP? OOP
AspectShallow CopyDeep Copy
Top-level objectNew object createdNew object created
Nested objectsReferences copied (shared)Recursively duplicated
Mutation riskMutating nested obj affects bothFully independent
PerformanceFasterSlower (recursive)
python
import copy

class Address:
    def __init__(self, city): self.city = city

class Person:
    def __init__(self, addr): self.addr = addr

p1 = Person(Address("NYC"))
p2 = copy.copy(p1)      # shallow — p2.addr is p1.addr
p3 = copy.deepcopy(p1)  # deep   — p3.addr is a new object

p2.addr.city = "LA"
print(p1.addr.city)  # LA  ← shallow copy shares nested obj
print(p3.addr.city)  # NYC ← deep copy is independent
15 What are metaclasses in Python? OOP
A metaclass is the "class of a class" — it defines how classes themselves behave, just as classes define how instances behave.

type is the default metaclass for all Python classes. Custom metaclasses inherit from type.
python
# Every class is an instance of its metaclass
print(type(int))    # <class 'type'>
print(type(type))   # <class 'type'>

# Custom metaclass: enforce method naming convention
class EnforceLower(type):
    def __new__(mcs, name, bases, namespace):
        for key in namespace:
            if key.startswith('_'): continue
            if key != key.lower():
                raise TypeError(f"Method '{key}' must be lowercase")
        return super().__new__(mcs, name, bases, namespace)

class MyModel(metaclass=EnforceLower):
    def save(self): pass       # OK
    # def Save(self): pass   # → TypeError
Use sparingly: Metaclasses are powerful but add significant complexity. ORMs (like Django), API frameworks, and dataclasses use them internally.
No questions match your search.