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
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.
Types: Compile-time (overloading), Runtime (overriding), Duck typing.
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!
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:
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).
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")
Python OOP
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 type | First arg | Use case |
|---|---|---|
| Instance method | self | Access/modify instance state |
| Class method | cls | Factory methods, alternative constructors |
| Static method | — | Utility 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__— indexingobj[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
▾
| Aspect | Abstract Class | Interface (Python pattern) |
|---|---|---|
| Concrete methods | Allowed | Not allowed (all abstract) |
| State | Can have instance variables | Typically no state |
| Multiple inherit | Possible but complex | Encouraged |
| Python syntax | ABC + mix of methods | ABC 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— noselforcls
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'
- 40–50% memory reduction when creating many instances
- Faster attribute access
Advanced OOP
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.
Python's solution: C3 Linearization creates a consistent, deterministic MRO.
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)
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).
Prefer composition when:
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()
- 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.
Use cases: Database connections, logging, configuration managers.
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
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
▾
| Aspect | Shallow Copy | Deep Copy |
|---|---|---|
| Top-level object | New object created | New object created |
| Nested objects | References copied (shared) | Recursively duplicated |
| Mutation risk | Mutating nested obj affects both | Fully independent |
| Performance | Faster | Slower (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.