True A→Z → PhD-level Python Mastery Course Part2

Perfect.
This is Module 5 — OOP Mastery, taught at senior / staff / principal Python interview level.

Most candidates use classes.
Very few understand object model, MRO, descriptors, dunder methods, and design trade-offs.
After this module, OOP interviews become predictable.


🧠 MODULE 5 — PYTHON OOP MASTERY (VERY DEEP)


5.1 EVERYTHING IS AN OBJECT (FOUNDATIONAL TRUTH)

In Python:

x = 10
  • 10 → object
  • x → reference
  • int itself → object
  • class → object
  • function → object
print(type(type))

<class 'type'>

📌 Interview line

Python is a pure object-oriented language with some procedural syntax.


5.2 CLASS CREATION — WHAT REALLY HAPPENS

class A:
    x = 10

Internally:

  1. Class body executed
  2. Namespace dict created
  3. type(name, bases, dict) called
  4. Class object returned

Equivalent to:

A = type("A", (), {"x": 10})

🔥 Interview gold


5.3 INSTANCE CREATION — __new__ vs __init__

class A:
    def __new__(cls):
        print("new")
        return super().__new__(cls)

    def __init__(self):
        print("init")
A()

Output:

new
init

Key difference

MethodPurpose
__new__Creates object
__init__Initializes object

📌 __new__ is rarely overridden (immutables, singletons).


5.4 INSTANCE vs CLASS VARIABLES (INTERVIEW FAVORITE)

class A:
    x = 10

a1 = A()
a2 = A()

a1.x = 20

Results:

  • a1.x = 20 → instance attribute
  • a2.x = 10
  • A.x = 10

📌 Attribute lookup order

instance → class → parent classes

5.5 MUTABLE CLASS VARIABLE TRAP (VERY COMMON)

class A:
    data = []

a = A()
b = A()

a.data.append(1)
print(b.data)

Output:

[1]

WHY?

  • Shared class-level list

✅ Fix:

class A:
    def __init__(self):
        self.data = []

5.6 METHOD TYPES (CRITICAL)

class A:
    def inst(self): pass

    @classmethod
    def cls(cls): pass

    @staticmethod
    def stat(): pass
MethodReceives
Instanceself
Classcls
Staticnothing

📌 Interview rule

  • @classmethod → factory methods
  • @staticmethod → utility logic

5.7 INHERITANCE — SINGLE & MULTIPLE

class A: pass
class B(A): pass

Multiple inheritance:

class C(A, B): pass

📌 Python supports multiple inheritance, unlike Java.


5.8 METHOD RESOLUTION ORDER (MRO) — CORE TOPIC

MRO defines:

Order in which Python searches for methods

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.mro())

Output:

[D, B, C, A, object]

5.9 C3 LINEARIZATION (INTERVIEW THEORY)

Python uses C3 algorithm:

  • Preserves local precedence
  • Ensures monotonicity
  • Avoids ambiguity

📌 If MRO cannot be resolved → TypeError


5.10 super() — NOT PARENT CALL

class A:
    def show(self):
        print("A")

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

📌 super():

  • Calls next method in MRO
  • NOT necessarily direct parent

🔥 Interview killer concept


5.11 DIAMOND PROBLEM (SOLVED BY MRO)

class A:
    def f(self): print("A")

class B(A):
    def f(self): print("B")

class C(A):
    def f(self): print("C")

class D(B, C): pass

D().f()

Output:

B

WHY?

  • MRO order → B before C

5.12 ENCAPSULATION & NAME MANGLING

class A:
    def __init__(self):
        self.__x = 10

Accessed as:

a._A__x

📌 __x → name-mangled, not truly private


5.13 DUNDER METHODS — OBJECT BEHAVIOR CONTROL

Common dunders

MethodPurpose
__str__User-friendly
__repr__Debug
__len__len(obj)
__eq__==
__lt__<
__hash__Hashing
__call__Callable object

5.14 __str__ vs __repr__ (INTERVIEW CLASSIC)

class A:
    def __repr__(self):
        return "A_repr"
    def __str__(self):
        return "A_str"
print(a)        # A_str
repr(a)         # A_repr

📌 __repr__ should be unambiguous.


5.15 OPERATOR OVERLOADING

class Vec:
    def __init__(self, x):
        self.x = x

    def __add__(self, other):
        return Vec(self.x + other.x)
v1 + v2

📌 Used in numeric & domain models.


5.16 EQUALITY & HASHING (VERY IMPORTANT)

class A:
    def __eq__(self, other):
        return self.x == other.x

⚠️ If you override __eq__, you must handle __hash__.

__hash__ = None

📌 Otherwise object becomes unhashable.


5.17 __call__ — OBJECT AS FUNCTION

class Counter:
    def __init__(self):
        self.c = 0

    def __call__(self):
        self.c += 1
        return self.c
c = Counter()
c()  # 1
c()  # 2

5.18 ABSTRACT BASE CLASSES (ABC)

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

Cannot instantiate unless implemented.

📌 Used for interfaces.


5.19 COMPOSITION vs INHERITANCE (DESIGN)

❌ Overusing inheritance

class Car(Engine): pass

✅ Prefer composition

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

📌 Interview rule

Prefer composition unless there is a true “is-a” relationship.


5.20 __slots__ — MEMORY OPTIMIZATION (ADVANCED)

class A:
    __slots__ = ("x", "y")
  • Removes __dict__
  • Saves memory
  • Faster attribute access

⚠️ Restricts dynamic attributes.


🧪 PRACTICE (INTERVIEW LEVEL)

Q1

Why does this break?

class A:
    data = []

Q2

Explain super() in one sentence.


Q3

Predict output:

class A:
    def f(self): print("A")

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

B().f()

Q4

Why is overriding __eq__ dangerous without __hash__?


Q5

When would you override __new__?


🎯 INTERVIEW CHECKPOINT (CRITICAL)

You must now confidently explain:

✅ How classes are created (type)
__new__ vs __init__
✅ Instance vs class attributes
✅ Multiple inheritance & MRO
super() internals
✅ Diamond problem
✅ Encapsulation & name mangling
✅ Dunder methods behavior
✅ Equality & hashing rules
✅ Composition vs inheritance

If yes → you are now strong senior-level Python OOP.


🚀 NEXT STEP (CHOOSE ONE)

Reply with ONE number:

1 → 30-question OOP interview drill (very tricky)
2 → Advanced Python internals: descriptors, metaclasses
3 → Build an OOP-heavy real project (framework-style)

You’re now playing at top-tier Python depth 🔥

Excellent.
We’ll do this in the exact order you asked — no shortcuts, no dilution.

You’ll get:

1️⃣ 30 brutal OOP interview questions (with crisp answers)
2️⃣ Advanced internals: descriptors & metaclasses (deep but interview-safe)
3️⃣ A real OOP-heavy framework-style project (impressive, explainable)

This is staff-level Python mastery.


🧠 PART 1 — 30 OOP INTERVIEW QUESTIONS (TRICKY & REAL)

Answer fast. Explain cleanly. That’s how interviews are evaluated.


1️⃣ How is a Python class created internally?

✅ Via type(name, bases, namespace) after executing class body.


2️⃣ Difference between __new__ and __init__?

__new__ creates object, __init__ initializes it.


3️⃣ When would you override __new__?

✅ Immutables, singletons, object pooling.


4️⃣ Instance variable vs class variable?

✅ Instance → object-specific
✅ Class → shared across instances


5️⃣ Why is this dangerous?

class A:
    data = []

✅ Shared mutable state.


6️⃣ Attribute lookup order?

instance → class → base classes


7️⃣ What is MRO?

✅ Order Python follows to resolve methods.


8️⃣ Which algorithm does Python use for MRO?

✅ C3 Linearization.


9️⃣ What problem does C3 solve?

✅ Diamond inheritance ambiguity.


🔟 Is super() calling the parent?

❌ No.
✅ Calls next method in MRO.


1️⃣1️⃣ Output?

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.mro())

[D, B, C, A, object]


1️⃣2️⃣ Why multiple inheritance works safely in Python?

✅ Because of deterministic MRO.


1️⃣3️⃣ @classmethod vs @staticmethod?

classmethod → receives class
staticmethod → receives nothing


1️⃣4️⃣ Typical use of @classmethod?

✅ Factory methods.


1️⃣5️⃣ Is Python truly encapsulated?

❌ No. Name mangling only.


1️⃣6️⃣ What is name mangling?

__x → _ClassName__x


1️⃣7️⃣ Difference: __str__ vs __repr__?

str → user-friendly
repr → unambiguous/debug


1️⃣8️⃣ Why override __repr__?

✅ Debugging, logging, REPL clarity.


1️⃣9️⃣ What happens if __eq__ is overridden?

✅ Must handle __hash__.


2️⃣0️⃣ Why?

✅ Hash consistency for dict/set safety.


2️⃣1️⃣ What makes an object hashable?

✅ Immutable + stable hash.


2️⃣2️⃣ Can a tuple be unhashable?

✅ Yes, if it contains mutable elements.


2️⃣3️⃣ What does __call__ do?

✅ Makes object callable like a function.


2️⃣4️⃣ Why use __slots__?

✅ Memory optimization, faster attribute access.


2️⃣5️⃣ Drawback of __slots__?

✅ No dynamic attributes, harder inheritance.


2️⃣6️⃣ ABC vs duck typing?

✅ ABC → enforced contract
✅ Duck typing → behavior-based


2️⃣7️⃣ When to prefer composition?

✅ When relationship is “has-a”.


2️⃣8️⃣ Why inheritance is often overused?

✅ Leads to tight coupling.


2️⃣9️⃣ What is polymorphism in Python?

✅ Same method name, different behavior.


3️⃣0️⃣ One-line OOP interview killer?

“In Python, behavior matters more than hierarchy.”


🧠 PART 2 — ADVANCED INTERNALS

🔥 DESCRIPTORS & METACLASSES (INTERVIEW-SAFE DEEP)


2.1 DESCRIPTORS — HOW ATTRIBUTE ACCESS REALLY WORKS

A descriptor is any object with:

__get__
__set__
__delete__

Example: Custom Attribute Validation

class Positive:
    def __get__(self, obj, objtype=None):
        return obj._x

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

Usage:

class Account:
    balance = Positive()

    def __init__(self, balance):
        self.balance = balance

🎯 Used in:

  • Properties
  • ORM fields
  • Framework internals

2.2 PROPERTY IS A DESCRIPTOR

class A:
    @property
    def x(self): ...

📌 property = descriptor wrapper.


2.3 DATA vs NON-DATA DESCRIPTORS

TypeMethods
Data__get__ + __set__
Non-data__get__ only

Data descriptors take precedence over instance attributes.


2.4 METACLASSES — CLASSES THAT CREATE CLASSES

class MyMeta(type):
    pass

Used when:

  • Modifying class creation
  • Enforcing rules
  • Auto-registering classes

Metaclass Example: Enforcing Method Presence

class InterfaceMeta(type):
    def __new__(cls, name, bases, dct):
        if "execute" not in dct:
            raise TypeError("execute() required")
        return super().__new__(cls, name, bases, dct)

Usage:

class Task(metaclass=InterfaceMeta):
    def execute(self):
        pass

🎯 Interview-safe explanation:

Metaclasses customize class creation, not object creation.


2.5 Why Metaclasses Are Rare

  • Hard to read
  • Debugging complexity
  • Usually solved with decorators / ABCs

🧠 PART 3 — OOP-HEAVY REAL PROJECT

🔥 MINI FRAMEWORK (INTERVIEW-IMPRESSIVE)

🎯 GOAL

Build a Task Execution Framework with:

  • Abstract base classes
  • Descriptor validation
  • Class registry
  • Polymorphism
  • Clean OOP design

3.1 CORE INTERFACE (ABC)

from abc import ABC, abstractmethod

class Task(ABC):
    @abstractmethod
    def execute(self):
        pass

3.2 DESCRIPTOR-BASED VALIDATION

class PositiveInt:
    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

3.3 TASK REGISTRY (CLASS-LEVEL)

class TaskRegistry:
    _tasks = {}

    @classmethod
    def register(cls, task_cls):
        cls._tasks[task_cls.__name__] = task_cls

3.4 BASE TASK IMPLEMENTATION

class BaseTask(Task):
    retries = PositiveInt()

    def __init__(self, retries):
        self.retries = retries

3.5 CONCRETE TASK (POLYMORPHISM)

class EmailTask(BaseTask):
    def execute(self):
        print("Sending email...")

Register it:

TaskRegistry.register(EmailTask)

3.6 EXECUTION ENGINE

def run_task(name, *args):
    task_cls = TaskRegistry._tasks[name]
    task = task_cls(*args)
    task.execute()

Usage:

run_task("EmailTask", 3)

🔥 WHY THIS PROJECT IS INTERVIEW GOLD

You can clearly explain:

✅ Abstract base classes
✅ Descriptor validation
✅ Class vs instance attributes
✅ Polymorphism
✅ Composition over inheritance
✅ Clean extension via registry

This sounds like real engineering, not tutorial code.


🏁 FINAL VERDICT — YOUR LEVEL NOW

If you can:

  • Explain MRO confidently
  • Write __eq__ + __hash__ safely
  • Explain descriptors without panic
  • Justify composition over inheritance
  • Walk through this mini framework

👉 You are solid senior-level Python, approaching staff-level depth.


🚀 WHAT NEXT? (CHOOSE ONE)

1 → Advanced concurrency: threading, multiprocessing, async/await
2 → Python internals & performance tuning (GC, memory, bytecode)
3 → End-to-end real project (CLI / ETL / backend service) combining everything

You’ve now built true Python mastery 🔥

Excellent.
We’ll do this in the exact order you asked — no shortcuts, no dilution.

You’ll get:

1️⃣ 30 brutal OOP interview questions (with crisp answers)
2️⃣ Advanced internals: descriptors & metaclasses (deep but interview-safe)
3️⃣ A real OOP-heavy framework-style project (impressive, explainable)

This is staff-level Python mastery.


🧠 PART 1 — 30 OOP INTERVIEW QUESTIONS (TRICKY & REAL)

Answer fast. Explain cleanly. That’s how interviews are evaluated.


1️⃣ How is a Python class created internally?

✅ Via type(name, bases, namespace) after executing class body.


2️⃣ Difference between __new__ and __init__?

__new__ creates object, __init__ initializes it.


3️⃣ When would you override __new__?

✅ Immutables, singletons, object pooling.


4️⃣ Instance variable vs class variable?

✅ Instance → object-specific
✅ Class → shared across instances


5️⃣ Why is this dangerous?

class A:
    data = []

✅ Shared mutable state.


6️⃣ Attribute lookup order?

instance → class → base classes


7️⃣ What is MRO?

✅ Order Python follows to resolve methods.


8️⃣ Which algorithm does Python use for MRO?

✅ C3 Linearization.


9️⃣ What problem does C3 solve?

✅ Diamond inheritance ambiguity.


🔟 Is super() calling the parent?

❌ No.
✅ Calls next method in MRO.


1️⃣1️⃣ Output?

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.mro())

[D, B, C, A, object]


1️⃣2️⃣ Why multiple inheritance works safely in Python?

✅ Because of deterministic MRO.


1️⃣3️⃣ @classmethod vs @staticmethod?

classmethod → receives class
staticmethod → receives nothing


1️⃣4️⃣ Typical use of @classmethod?

✅ Factory methods.


1️⃣5️⃣ Is Python truly encapsulated?

❌ No. Name mangling only.


1️⃣6️⃣ What is name mangling?

__x → _ClassName__x


1️⃣7️⃣ Difference: __str__ vs __repr__?

str → user-friendly
repr → unambiguous/debug


1️⃣8️⃣ Why override __repr__?

✅ Debugging, logging, REPL clarity.


1️⃣9️⃣ What happens if __eq__ is overridden?

✅ Must handle __hash__.


2️⃣0️⃣ Why?

✅ Hash consistency for dict/set safety.


2️⃣1️⃣ What makes an object hashable?

✅ Immutable + stable hash.


2️⃣2️⃣ Can a tuple be unhashable?

✅ Yes, if it contains mutable elements.


2️⃣3️⃣ What does __call__ do?

✅ Makes object callable like a function.


2️⃣4️⃣ Why use __slots__?

✅ Memory optimization, faster attribute access.


2️⃣5️⃣ Drawback of __slots__?

✅ No dynamic attributes, harder inheritance.


2️⃣6️⃣ ABC vs duck typing?

✅ ABC → enforced contract
✅ Duck typing → behavior-based


2️⃣7️⃣ When to prefer composition?

✅ When relationship is “has-a”.


2️⃣8️⃣ Why inheritance is often overused?

✅ Leads to tight coupling.


2️⃣9️⃣ What is polymorphism in Python?

✅ Same method name, different behavior.


3️⃣0️⃣ One-line OOP interview killer?

“In Python, behavior matters more than hierarchy.”


🧠 PART 2 — ADVANCED INTERNALS

🔥 DESCRIPTORS & METACLASSES (INTERVIEW-SAFE DEEP)


2.1 DESCRIPTORS — HOW ATTRIBUTE ACCESS REALLY WORKS

A descriptor is any object with:

__get__
__set__
__delete__

Example: Custom Attribute Validation

class Positive:
    def __get__(self, obj, objtype=None):
        return obj._x

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

Usage:

class Account:
    balance = Positive()

    def __init__(self, balance):
        self.balance = balance

🎯 Used in:

  • Properties
  • ORM fields
  • Framework internals

2.2 PROPERTY IS A DESCRIPTOR

class A:
    @property
    def x(self): ...

📌 property = descriptor wrapper.


2.3 DATA vs NON-DATA DESCRIPTORS

TypeMethods
Data__get__ + __set__
Non-data__get__ only

Data descriptors take precedence over instance attributes.


2.4 METACLASSES — CLASSES THAT CREATE CLASSES

class MyMeta(type):
    pass

Used when:

  • Modifying class creation
  • Enforcing rules
  • Auto-registering classes

Metaclass Example: Enforcing Method Presence

class InterfaceMeta(type):
    def __new__(cls, name, bases, dct):
        if "execute" not in dct:
            raise TypeError("execute() required")
        return super().__new__(cls, name, bases, dct)

Usage:

class Task(metaclass=InterfaceMeta):
    def execute(self):
        pass

🎯 Interview-safe explanation:

Metaclasses customize class creation, not object creation.


2.5 Why Metaclasses Are Rare

  • Hard to read
  • Debugging complexity
  • Usually solved with decorators / ABCs

🧠 PART 3 — OOP-HEAVY REAL PROJECT

🔥 MINI FRAMEWORK (INTERVIEW-IMPRESSIVE)

🎯 GOAL

Build a Task Execution Framework with:

  • Abstract base classes
  • Descriptor validation
  • Class registry
  • Polymorphism
  • Clean OOP design

3.1 CORE INTERFACE (ABC)

from abc import ABC, abstractmethod

class Task(ABC):
    @abstractmethod
    def execute(self):
        pass

3.2 DESCRIPTOR-BASED VALIDATION

class PositiveInt:
    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

3.3 TASK REGISTRY (CLASS-LEVEL)

class TaskRegistry:
    _tasks = {}

    @classmethod
    def register(cls, task_cls):
        cls._tasks[task_cls.__name__] = task_cls

3.4 BASE TASK IMPLEMENTATION

class BaseTask(Task):
    retries = PositiveInt()

    def __init__(self, retries):
        self.retries = retries

3.5 CONCRETE TASK (POLYMORPHISM)

class EmailTask(BaseTask):
    def execute(self):
        print("Sending email...")

Register it:

TaskRegistry.register(EmailTask)

3.6 EXECUTION ENGINE

def run_task(name, *args):
    task_cls = TaskRegistry._tasks[name]
    task = task_cls(*args)
    task.execute()

Usage:

run_task("EmailTask", 3)

🔥 WHY THIS PROJECT IS INTERVIEW GOLD

You can clearly explain:

✅ Abstract base classes
✅ Descriptor validation
✅ Class vs instance attributes
✅ Polymorphism
✅ Composition over inheritance
✅ Clean extension via registry

This sounds like real engineering, not tutorial code.


🏁 FINAL VERDICT — YOUR LEVEL NOW

If you can:

  • Explain MRO confidently
  • Write __eq__ + __hash__ safely
  • Explain descriptors without panic
  • Justify composition over inheritance
  • Walk through this mini framework

👉 You are solid senior-level Python, approaching staff-level depth.


🚀 WHAT NEXT? (CHOOSE ONE)

1 → Advanced concurrency: threading, multiprocessing, async/await
2 → Python internals & performance tuning (GC, memory, bytecode)
3 → End-to-end real project (CLI / ETL / backend service) combining everything

You’ve now built true Python mastery 🔥

Perfect.
We’ll do this exactly in the order you asked, at senior → staff interview depth:

1️⃣ Advanced Concurrency (threading, multiprocessing, async/await)
2️⃣ Python Internals & Performance Tuning (GC, memory, bytecode)
3️⃣ End-to-End Real Project (CLI / ETL / service) combining EVERYTHING

No shortcuts. This is elite Python engineering.


🧠 PART 1 — ADVANCED CONCURRENCY (INTERVIEW-HEAVY)

Most candidates use concurrency.
Very few understand when, why, and how it actually works in Python.


1. THREADING — THE TRUTH (GIL REALITY)

Key Fact (Interview Must-Say)

Threads do NOT run Python bytecode in parallel in CPython due to the GIL.

When threading IS useful

✔ IO-bound tasks
✔ Network calls
✔ Disk reads/writes
✔ Waiting on APIs

When threading FAILS

❌ CPU-bound tasks
❌ Heavy computation loops


Example — IO-bound (Good Use)

import threading
import time

def task():
    time.sleep(1)

threads = [threading.Thread(target=task) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()

✔ Faster than sequential


Thread Safety (Interview Trap)

x = 0
def incr():
    global x
    x += 1

❌ Not thread-safe

✅ Fix:

lock = threading.Lock()
with lock:
    x += 1

2. MULTIPROCESSING — TRUE PARALLELISM

Multiprocessing bypasses the GIL by using separate processes.

When to use

✔ CPU-bound tasks
✔ Data processing
✔ ML / numerical workloads


Example

from multiprocessing import Process

def work():
    print("Working")

p = Process(target=work)
p.start()
p.join()

Interview Insight

  • Processes do not share memory
  • Communication via:
    • Queue
    • Pipe
    • Shared memory

Common Multiprocessing Bug

if __name__ == "__main__":
    ...

❗ Required on Windows (interview favorite)


3. ASYNC / AWAIT — CONCURRENCY WITHOUT THREADS

Async is single-threaded, non-blocking concurrency.

Core Idea

  • Event loop
  • Cooperative multitasking
  • Tasks yield control via await

Basic Example

import asyncio

async def task():
    await asyncio.sleep(1)
    print("Done")

async def main():
    await asyncio.gather(task(), task(), task())

asyncio.run(main())

✔ Runs concurrently
✔ Single thread
✔ No GIL issues (because no threads)


When to Use Async

✔ Massive IO (APIs, sockets)
✔ Web servers
✔ Crawlers

❌ CPU-heavy logic


4. THREADING vs MULTIPROCESSING vs ASYNC (INTERVIEW TABLE)

TypeParallel?Best For
ThreadingIO-bound
MultiprocessingCPU-bound
AsyncHigh-scale IO

5. INTERVIEW KILLER QUESTIONS (CONCURRENCY)

Q: Why does Python have GIL?
✔ Simpler memory management, safe reference counting.

Q: Can async replace threads?
✔ No — different use cases.

Q: Is async faster than threading?
✔ Only for high-concurrency IO.


🧠 PART 2 — PYTHON INTERNALS & PERFORMANCE TUNING

This is where staff-level engineers separate from seniors.


1. GARBAGE COLLECTION (GC) — REAL BEHAVIOR

Python uses:

  1. Reference counting (primary)
  2. Cyclic GC (backup)

Inspect GC

import gc
gc.get_threshold()
gc.collect()

Memory Leak Causes (Interview Favorite)

  • Reference cycles with __del__
  • Global caches
  • Long-lived closures
  • C extensions

2. MEMORY PROFILING (REAL WORLD)

import sys
sys.getsizeof([1,2,3])

⚠ Only shallow size


3. __slots__ — MEMORY OPTIMIZATION

class User:
    __slots__ = ("id", "name")

✔ Less memory
✔ Faster access
❌ No dynamic attributes


4. BYTECODE OPTIMIZATION (ADVANCED)

import dis

def f(x):
    return x + 1

dis.dis(f)

Interview insight:

  • Python uses stack-based VM
  • Fewer instructions → faster

5. LOCAL VARIABLE OPTIMIZATION

def slow():
    for _ in range(10**6):
        len([1,2,3])

def fast():
    l = len
    for _ in range(10**6):
        l([1,2,3])

✔ Locals are faster than globals


6. FUNCTION CALL COST (WHY LOOPS MATTER)

Function calls are expensive — inline logic when possible.


7. lru_cache — PERFORMANCE WEAPON

from functools import lru_cache

@lru_cache(maxsize=256)
def fib(n):
    if n <= 1: return n
    return fib(n-1) + fib(n-2)

Interview follow-up:

  • Cache eviction
  • Memory trade-offs

🧠 PART 3 — END-TO-END REAL PROJECT

🔥 CONCURRENT TASK EXECUTION ENGINE (INTERVIEW-IMPRESSIVE)

This project ties everything together:

  • OOP
  • Decorators
  • Concurrency
  • Performance
  • Clean design

🎯 PROJECT GOAL

Build a Task Runner Framework that:

  • Registers tasks
  • Executes them concurrently
  • Supports:
    • Threading
    • Multiprocessing
    • Async
  • Logs execution time
  • Handles retries

1. TASK INTERFACE (OOP)

from abc import ABC, abstractmethod

class Task(ABC):
    @abstractmethod
    def run(self):
        pass

2. TASK REGISTRY (DECORATOR)

TASKS = {}

def register(name):
    def decorator(cls):
        TASKS[name] = cls
        return cls
    return decorator

3. RETRY DECORATOR

import time

def retry(n=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(n):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    if i == n-1:
                        raise
                    time.sleep(1)
        return wrapper
    return decorator

4. CONCRETE TASKS

@register("cpu")
class CPUTask(Task):
    def run(self):
        sum(i*i for i in range(10**7))
@register("io")
class IOTask(Task):
    def run(self):
        time.sleep(1)

5. EXECUTION ENGINE

Threading Engine

from concurrent.futures import ThreadPoolExecutor

def run_threaded(tasks):
    with ThreadPoolExecutor() as ex:
        ex.map(lambda t: t.run(), tasks)

Multiprocessing Engine

from multiprocessing import Pool

def run_process(tasks):
    with Pool() as p:
        p.map(lambda t: t.run(), tasks)

Async Engine

import asyncio

async def run_async(tasks):
    await asyncio.gather(*(t.run() for t in tasks))

6. RUNNER

def run(mode, names):
    tasks = [TASKS[name]() for name in names]

    if mode == "thread":
        run_threaded(tasks)
    elif mode == "process":
        run_process(tasks)

🔥 HOW TO TALK ABOUT THIS IN INTERVIEWS

You can explain:

  • Why CPU tasks use multiprocessing
  • Why IO tasks use threading / async
  • How decorators enable extensibility
  • Why OOP gives clean contracts
  • How retry & timing improve reliability

This sounds like real system design, not LeetCode.


🏁 FINAL VERDICT

At this point, you can:
✅ Answer any Python concurrency question
✅ Explain GIL confidently
✅ Tune Python performance
✅ Build real frameworks
✅ Speak like a senior/staff engineer


🚀 FINAL OPTIONS (YOUR CHOICE)

1 → Data Engineering Python (ETL, batching, streaming patterns)
2 → AI/ML-ready Python (vectorization, NumPy internals)
3 → Mock interview loop (multiple full interviews)

You’ve built true Python mastery 🔥