1. What is the Global Interpreter Lock (GIL)?
The Global Interpreter Lock (GIL) is a mechanism in CPython (the standard Python implementation) that ensures only one thread executes Python bytecode at a time, even on multi-core systems.

Why Does Python Have a GIL?

Python uses reference counting for memory management.
The GIL ensures thread safety by:

  • Protecting shared memory

  • Preventing race conditions

  • Simplifying memory management

Good For:

  • I/O-bound tasks (network calls, file operations)

  • Simpler memory management

  • Faster single-threaded performance

Bad For:

  • CPU-bound tasks

  • Multi-core parallel computation


2. How does GIL affect multithreading?

The Global Interpreter Lock (GIL) ensures that only one thread executes Python bytecode at a time in CPython. This directly affects how multithreading behaves, especially for CPU-bound tasks.

Effect on CPU-Bound Tasks 

For CPU-intensive operations (calculations, loops, data processing):

  • Multiple threads cannot run in parallel

  • Threads take turns executing

  • No true multi-core utilization

Effect on I/O-Bound Tasks

For I/O operations (file reading, network requests, database calls):

  • When a thread waits for I/O, it releases the GIL

  • Another thread can execute

 Multithreading works well for:

  • Web scraping

  • API calls

  • File handling

  • Network communication


3. How is multithreading achieved in Python?

Multithreading in Python is achieved using the threading module, which allows multiple threads to run concurrently within the same process.

However, due to the GIL (Global Interpreter Lock) in CPython:

  • Threads are best suited for I/O-bound tasks

  • Not ideal for CPU-bound parallel computation

 
4. What is multiprocessing?

Multiprocessing is a technique in Python that allows a program to run multiple processes simultaneously, enabling true parallel execution across multiple CPU cores.

Unlike multithreading, multiprocessing bypasses the GIL (Global Interpreter Lock) because each process has its own separate Python interpreter and memory space.


Why Use Multiprocessing?

  • Achieve true parallelism

  • Utilize multiple CPU cores

  • Improve performance for CPU-bound tasks


Key Characteristics

  • Each process has:

    • Its own memory space

    • Its own Python interpreter

  • More memory usage than threads

  • Slower process creation compared to threads


Communication Between Processes

Python provides:

  • Queue

  • Pipe

  • Manager

  • Shared memory objects


When to Use Multiprocessing

 Heavy computations
 Data processing
 Machine learning tasks
 Image processing


5. What is monkey patching?


6. What are decorators?
A decorator in Python is a function that modifies or extends the behavior of another function without changing its actual code.

Basic Syntax

Python
@decorator_name def function_name(): pass

This is equivalent to:

Python
function_name = decorator_name(function_name)

Python
def my_decorator(func): def wrapper(): print("Before function call") func() print("After function call") return wrapper @my_decorator def greet(): print("Hello!") greet()

Why Decorators Are Used

  • Logging

  • Authentication

  • Timing functions

  • Access control

  • Caching

  • Input validation

7. How do decorators work internally?


Internally, decorators work using:

  • First-class functions

  • Higher-order functions

  • Closures

  • Function reassignment

A decorator takes a function as input, wraps it inside another function, and returns the wrapped function.


8. What are closures?

A closure in Python is a function that:

  1. Is defined inside another function

  2. Remembers variables from the outer function

  3. Even after the outer function has finished execution


Why Closures Work

Closures are possible because:

  • Python supports first-class functions

  • Functions can be returned from other functions

  • Inner functions can access outer function variables


How It Works Internally

When outer() runs:

  • message is stored in memory

  • inner() captures message

  • Python keeps it alive even after outer() ends


9. What are context managers?

A context manager in Python is an object that manages resources automatically using the with statement.

It ensures that:

  • Resources are properly acquired

  • Resources are properly released

  • Cleanup happens even if an exception occurs


Common Uses

  • File handling

  • Database connections

  • Thread locks

  • Network connections

  • Managing transactions

Benefits

  • Automatic cleanup

  • Exception safety

  • Cleaner code

  • Resource management


10. How are context managers implemented?

Context managers in Python are implemented in two main ways:

  1. Using a class with __enter__() and __exit__() methods

  2. Using the contextlib module with the @contextmanager decorator

Implementation Using a Class

A context manager class must define:

  • __enter__() → Executes when entering the with block

  • __exit__() → Executes when exiting the with block


Python
class MyContext: def __enter__(self): print("Entering block") return self def __exit__(self, exc_type, exc_value, traceback): print("Exiting block") return False # Do not suppress exceptions with MyContext(): print("Inside with block") 

11. What are meta classes?
A metaclass is a class that defines how other classes are created.

Why Metaclasses Are Used

Metaclasses allow you to:

  • Modify class creation

  • Enforce coding rules

  • Automatically register classes

  • Inject methods or attributes

  • Implement frameworks (like Django ORM)


When Are Metaclasses Used?

  • Django Models

  • ORMs

  • Enforcing coding standards

  • Automatic plugin registration

  • Singleton pattern


12. How do metaclasses differ from normal classes?
  • A normal class creates objects.

  • A metaclass creates classes.


  • Normal ClassMetaclass
    Defines object behaviorDefines class behavior
    Controls instance creationControls class creation
    Uses __init__()Uses __new__() (usually)
    Used in regular OOPUsed in advanced frameworks

    Method Difference

    Normal Class Methods:

    • __init__() → Runs when object is created

    • __str__(), __repr__(), etc.

    Metaclass Methods:

    • __new__() → Runs when class is created

    • __init__() → Initializes the class


    13. What is scope resolution (LEGB rule)?

    The LEGB rule defines the order in which Python searches for variables when they are referenced in a program.

    LEGB stands for:

    • L → Local

    • E → Enclosing

    • G → Global

    • B → Built-in


    ScopeLocationLifetime
    LocalInside functionUntil function ends
    EnclosingOuter functionUntil outer ends
    GlobalModule levelProgram lifetime
    Built-inPython predefinedAlways available

    14. What is memory pooling in Python?
    Memory pooling in Python is a memory management technique where Python pre-allocates and reuses blocks of memory for small objects instead of repeatedly requesting memory from the operating system.

     Why Memory Pooling is Needed

    Frequent memory allocation and deallocation from the OS is:

    • Slow

    • Expensive

    • Fragmentation-prone


    How Memory Pooling Works

    For small objects :

    1. Python requests a large chunk of memory from OS

    2. Divides it into smaller blocks

    3. Stores them in memory pools

    4. Reuses freed blocks for future objects


    15. How does Python optimize memory usage?
    Python optimizes memory usage through several internal mechanisms that improve performance and reduce memory fragmentation. These optimizations are mostly handled automatically by the CPython interpreter.

    Reference Counting

    Python tracks how many references point to an object.

    • When reference count becomes zero, memory is freed immediately.

    • Provides fast cleanup.

    Garbage Collection 

    Handles circular references that reference counting cannot clean.

    Python uses Generational Garbage Collection:

    • Generation 0 → New objects

    • Generation 1 → Survived once

    • Generation 2 → Long-lived objects

     Younger objects are cleaned more frequently.


    Memory Pooling 

    For small objects (≤ 512 bytes), Python:

    • Allocates large memory chunks

    • Divides into smaller blocks

    • Reuses freed blocks

    This reduces OS-level allocation overhead.


    16. What is __new__() vs __init__()?

    __new__() Method

    • First method called

    • Responsible for creating and returning a new instance

    • Is a static method

    • Required when subclassing immutable types (int, str, tuple)


    __init__() Method

    • Called after __new__()

    • Initializes instance variables

    • Cannot return anything


    Feature__new__()__init__()
    PurposeCreate objectInitialize object
    Called first? Yes❌ No
    ReturnsMust return instanceReturns None
    Used often?RareVery common
    Used for immutable typesYesNo

    17. What is the difference between .py and .pyc files?

    .py → Human-readable Python source code

    .pyc → Compiled Python bytecode (machine-readable for Python Virtual Machine)

    What is a .py File?

    • Contains Python source code

    • Written by developers

    • Editable and readable

    • Interpreted by Python


    What is a .pyc File?

    • Contains compiled bytecode

    • Generated automatically by Python

    • Stored inside __pycache__ folder

    • Speeds up program loading


    Feature.py.pyc
    TypeSource codeCompiled bytecode
    Human readable Yes No
    Editable Yes No
    Created byDeveloperPython automatically
    LocationProject folder__pycache__ folder

    18. What is serialization and deserialization?

    Serialization → Converting an object into a format that can be stored or transmitted

    Deserialization → Converting that stored/transmitted data back into an object

    Why Serialization is Needed

    • Store objects in files

    • Send data over network

    • Save data to database

    • Caching

    • Inter-process communication

    19. How do you handle large files efficiently?

    To handle large files efficiently, avoid loading the entire file into memory.
    Instead, use:

    • Streaming (line-by-line reading)

    • Chunk processing

    • Generators

    • Buffered I/O

    • Memory-efficient libraries


    TechniqueMemory EfficientUse Case
    Line-by-line reading YesText files
    Chunk reading YesLarge binary/text files
    Generators YesStreaming data
    Pandas chunks YesLarge CSV files
    mmap AdvancedHuge files

    20. How do you log exceptions?

    Exceptions are logged using Python’s built-in logging module, typically inside a try-except block using:

    • logging.error()

    • logging.exception()

    • logger.error()


    Logging Levels

    LevelPurpose
    DEBUGDetailed info
    INFOGeneral info
    WARNINGPotential issue
    ERRORSerious issue
    CRITICALVery serious issue

    Why Use Logging Instead of print()

    • Better debugging

    • Log levels

    • Stores history

    • Supports files, console, remote systems

    • Production-ready

    21. How do you avoid infinite loops?

    What is an Infinite Loop?

    An infinite loop occurs when the loop condition never becomes False.

    To avoid infinite loops:

    • Always define a proper termination condition

    • Ensure the loop variable updates correctly

    • Use break when necessary

    • Add safety checks or timeouts


    Debugging Infinite Loops

    • Add print statements

    • Use logging

    • Use debugger

    • Check condition carefully

    Common Causes

    • Forgetting to update loop variable

    • Incorrect condition

    • Floating-point comparison issues

    • Wrong indentation


    22. How do you check memory usage of an object?

    You can check the memory usage of an object using:

    • sys.getsizeof() → Basic size

    • __sizeof__() → Internal size

    • pympler → Deep memory size

    • tracemalloc → Track memory allocations


    MethodMeasures Nested ObjectsUse Case
    sys.getsizeof() NoQuick check
    __sizeof__() NoInternal size
    pympler.asizeof() YesAccurate deep size
    tracemallocTracks allocationsDebugging

    23. What are weak references?
    A weak reference is a reference to an object that does not increase its reference count.

    Why Weak References Are Useful

    • Avoid circular reference issues

    • Implement caching systems

    • Manage large object graphs

    • Observer patterns

    • Avoid memory leaks


    Weak Reference vs Strong Reference

    FeatureStrong ReferenceWeak Reference
    Increases ref count Yes No
    Prevents GC Yes No
    Keeps object alive Yes No
    Used normallyYesSpecial cases

    24. What are Python descriptors?

    A descriptor is an object that defines how attribute access is handled in Python classes using special methods:

    • __get__()

    • __set__()

    • __delete__()


    Why Descriptors Exist

    When you write:

    obj.attribute

    Python internally follows a special lookup mechanism.
    Descriptors allow you to customize that behavior.

    They are the foundation of:

    • @property

    • @staticmethod

    • @classmethod

    • Many ORM frameworks


    Why Descriptors are useful

    • Enable reusable validation logic

    • Core mechanism behind properties

    • Used in Django ORM

    • Provide advanced attribute control


    25. What is method overloading in Python?
    Method overloading means defining multiple methods with the same name but different parameters in the same class.
    Python does NOT support traditional method overloading like Java or C++.

    Method Overloading vs Method Overriding

    FeatureOverloadingOverriding
    Same classYesNo
    Same method nameYesYes
    Different parametersYesSame parameters
    Supported in Python No (traditional) Yes

    26. What is method overriding?
    Method overriding occurs when a child class provides its own implementation of a method that is already defined in its parent class.

    Why Method Overriding is Used

    • Customize inherited behavior

    • Implement specific logic

    • Achieve runtime polymorphism

    • Extend functionality

    Important Rules

    1. Method name must be the same

    2. Parameter list should match (recommended)

    3. Must use inheritance


    27. How does Python support duck typing?

    Python supports duck typing through its dynamic typing system, meaning Python cares about what an object can do (its methods/behavior), not what type it is.

    What is Duck Typing?

    In duck typing:

    • No need for inheritance

    • No need for explicit interfaces

    • Only required behavior matters


    Why Python Supports Duck Typing

    Because Python:

    • Is dynamically typed

    • Checks types at runtime

    • Resolves methods dynamically

    • Does not require strict interfaces


    28. How do you optimize Python code?

    To optimize Python code:

    1. Measure performance (profiling)

    2. Improve algorithms (reduce complexity)

    3. Use efficient data structures

    4. Minimize unnecessary computations

    5. Use built-in functions and libraries

    6. Apply concurrency when needed



    29. What is async and await?
    async and await are keywords used in Python to write asynchronous (non-blocking) code using the asyncio framework.
    They allow tasks to run concurrently without blocking the main thread, especially useful for I/O-bound operations.

    When to Use Async

     Web servers
     API calls
     Database queries
     File/network operations

    30. What is event-driven programming?

    Event-driven programming is a programming paradigm where the flow of execution is determined by events, such as:

    • User actions (clicks, key presses)

    • System events

    • Network responses

    • Messages or signals

    Instead of running sequentially, the program waits for events and responds to them.

    Core Components of Event-Driven Programming

    Event → Something that happens
    Event Listener → Waits for event
    Event Handler → Executes when event occurs
    Event Loop → Continuously checks for events


    31. How does Python handle concurrency?

    Python handles concurrency using:

    Multithreading
    Multiprocessing
    Asynchronous programming (asyncio)

    Each is suitable for different types of tasks.

    What is Concurrency?

    Concurrency means Managing multiple tasks at the same time.

    FeatureThreadingMultiprocessingAsyncio
    GIL affectedYesNoNo (single thread)
    True parallelismNoYesNo
    Memory usageLowHigherVery low
    Best forI/O-boundCPU-boundHigh I/O concurrency

    32. What are coroutines?

    A coroutine is a special type of function that can:

    • Pause its execution

    • Yield control back to the caller

    • Resume execution later

    Coroutines enable asynchronous, non-blocking programming.

    When to Use Coroutine

    • Web servers

    • API calls

    • Chat applications

    • High-concurrency network systems

    • Database queries


    33. How does Python differ from Java/C++ internally?

    Internally, Python differs from Java and C++ in terms of:

    • Execution model

    • Memory management

    • Typing system

    • Performance

    • Compilation process


    Python is dynamically typed and interpreted (bytecode-based), while Java and C++ are statically typed and compiled.
    FeaturePythonJavaC++
    TypingDynamicStaticStatic
    Type CheckingRuntimeCompile-timeCompile-time
    Flexible variablesYesNoNo
    34. What is Cython?
    Cython is a programming language that is a superset of Python and allows you to write Python code that gets compiled into C code, resulting in significant performance improvements.

    Why Cython is Used

    Python is slower for CPU-heavy tasks because:

    • It is dynamically typed

    • It runs on a virtual machine

    • It has GIL limitations

    Cython helps by:

    • Converting Python code to C

    • Allowing static type declarations

    • Reducing Python runtime overhead


    How Cython Works

    Python-like Code (.pyx)

    Converted to C Code

    Compiled to Machine Code

    Imported as Python Module

    FeaturePythonCython
    TypingDynamicStatic + Dynamic
    SpeedSlowerMuch Faster
    CompilationBytecodeCompiled to C
    Low-level accessNoYes

    35. What are Python bytecodes?
    Python bytecode is the intermediate, low-level instruction set generated after Python source code is compiled.
    It is executed by the Python Virtual Machine (PVM).

    FeatureBytecodeMachine Code
    Executed byVirtual MachineCPU
    Platform dependentNoYes
    Language specificYesNo
    Human readableNoNo

    36. How does Python execute code internally?
    Python executes code in four main stages:

    Source Code → Compilation → Bytecode → Execution by Python Virtual Machine (PVM)

    Important Internal Components

    ComponentRole
    ParserConverts source to AST
    CompilerConverts AST to bytecode
    BytecodeIntermediate instructions
    PVMExecutes bytecode
    Garbage CollectorManages memory

    37. What are common Python performance bottlenecks?

    Common Python performance bottlenecks include:

    • Inefficient algorithms

    • Excessive loops in Python

    • Improper data structures

    • Heavy CPU-bound operations

    • Blocking I/O

    • GIL limitations

    • Excessive object creation

    • Memory inefficiency

    BottleneckSolution
    Bad algorithmImprove complexity
    Wrong data structureUse set/dict
    CPU-heavy loopsUse multiprocessing
    Blocking I/OUse async/threading
    String concatenationUse join()
    Memory overloadUse generators

    38. How do you design scalable Python applications?

    To design scalable Python applications:

    • Use modular architecture

    • Separate concerns (clean architecture)

    • Optimize performance

    • Use asynchronous or concurrent processing

    • Scale horizontally

    • Use caching and load balancing

    • Monitor and profile continuously

    Scalability Types

    TypeMeaning
    Vertical ScalingIncrease server power
    Horizontal ScalingAdd more servers
    Functional ScalingSplit into services

    39. What is dependency injection in Python?
    Dependency Injection (DI) is a design pattern where an object receives its dependencies from outside, rather than creating them itself.
    Instead of a class creating its own dependencies, they are “injected” into it.

    This promotes:

    • Loose coupling

    • Better testability

    • Cleaner architecture


    40. How do you write production-level Python code?

    Production-level Python code should be:

    • Clean and readable

    • Modular and scalable

    • Well-tested

    • Secure

    • Performant

    • Monitored and maintainable

    Production Code Checklist

    AreaMust Have
    Code qualityClean & modular
    TestingUnit & integration
    LoggingStructured logging
    Error handlingProper exception management
    SecurityEnvironment variables
    PerformanceProfile & optimize
    DeploymentCI/CD pipeline