InterviewQAs

Python Interview Questions

PIQ
Python Interview Questions

Python interviews at experienced levels rarely focus on syntax alone. Most companies evaluate how developers handle memory usage, concurrency, API integrations, testing, data processing, packaging, and debugging under production constraints. Questions often revolve around practical engineering decisions rather than textbook definitions.

Modern Python development spans backend APIs, automation, ETL systems, machine learning pipelines, cloud tooling, and asynchronous applications. A strong candidate is expected to understand not only how features work, but also where they break, what performance tradeoffs exist, and how maintainability is affected over time.

Interviewers increasingly use scenario-driven questions. Instead of asking what a decorator is, they may ask how decorators are used for rate limiting, tracing, retries, or authentication in production systems. Likewise, generators are discussed in the context of large datasets and streaming pipelines rather than simple examples.

Concurrency and optimization are also common focus areas. Understanding the Global Interpreter Lock, async event loops, multiprocessing behavior, caching strategies, and memory-efficient iteration patterns can significantly impact how Python applications scale under load.

Question 01

Why are Python generators preferred in large-scale ETL or streaming applications?

MEDIUM

Generators allow data to be processed lazily instead of loading everything into memory at once. In ETL pipelines handling millions of rows, reading entire datasets into lists can quickly exhaust RAM and slow down processing. A generator yields one item at a time, making memory consumption predictable even when datasets are extremely large.

In production systems, generators are commonly used while reading log files, consuming Kafka messages, processing CSV exports, or streaming API responses. They integrate naturally with pipelines because downstream consumers can process records immediately without waiting for the full dataset to load.

Another practical advantage is composability. Multiple generators can be chained together for filtering, transformation, validation, and enrichment. This pattern reduces temporary data structures and often improves overall throughput in data engineering workloads.

Question 02

Which statements about Python's Global Interpreter Lock (GIL) are correct?

MEDIUM
  • A The GIL prevents multiple native threads from executing Python bytecode simultaneously.
  • B Multiprocessing can bypass GIL limitations for CPU-intensive workloads.
  • C The GIL improves performance for all multithreaded applications.
  • D I/O-bound applications can still benefit from threading despite the GIL.

The GIL is one of the most misunderstood parts of Python interviews. It mainly impacts CPU-bound threaded workloads because only one thread executes Python bytecode at a time inside a process.

For CPU-intensive workloads like image processing or numerical computation, multiprocessing is commonly used because each process gets its own Python interpreter and GIL. However, for I/O-heavy applications such as API calls, database access, or network communication, threads still provide concurrency because the interpreter releases the GIL during blocking I/O operations.

Question 03

Write a Python function to count word frequency in a text file efficiently.

EASY

This implementation processes the file line by line instead of loading the entire file into memory. That becomes important when handling multi-gigabyte logs or exported datasets.

The Counter class simplifies frequency aggregation and performs well for text analytics tasks. Similar approaches are commonly used in log analysis, NLP preprocessing, and reporting pipelines.

# Python
from collections import Counter


def count_words(file_path):
    counter = Counter()

    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            words = line.strip().lower().split()
            counter.update(words)

    return counter


if __name__ == '__main__':
    result = count_words('sample.txt')
    print(result.most_common(10))
Question 04

What are the practical differences between multiprocessing and multithreading in Python?

HARD

Multithreading is generally used for I/O-bound workloads such as HTTP requests, file operations, or database calls. Threads share the same memory space, which makes communication easier but also introduces synchronization concerns like race conditions and deadlocks.

Multiprocessing creates separate processes with independent memory spaces. This avoids the GIL limitation for CPU-intensive workloads such as image transformations, machine learning preprocessing, or mathematical computations. The tradeoff is higher memory usage and slower inter-process communication.

In production systems, the choice depends on workload characteristics. A web scraper making thousands of API calls may benefit from threading or asyncio, while a video rendering pipeline often benefits more from multiprocessing.

Question 05

Which data structure provides average O(1) lookup performance in Python?

EASY
  • A list
  • B tuple
  • C dict
  • D set

Both dictionaries and sets are implemented using hash tables internally, which gives them average constant-time lookup performance.

Lists and tuples require sequential traversal for searches unless additional indexing structures are used. In large systems, choosing the right data structure can significantly reduce execution time.

Question 06

Write a decorator that measures execution time of any function.

MEDIUM

Decorators are heavily used in production Python frameworks for logging, authentication, retries, caching, monitoring, and tracing. Timing decorators are especially useful for identifying bottlenecks in APIs or batch jobs.

Using wraps preserves the original function metadata such as the name and docstring, which is important for debugging tools and documentation generators.

# Python
import time
from functools import wraps


def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()

        print(f'{func.__name__} executed in {end - start:.4f} seconds')
        return result

    return wrapper


@timing_decorator
def process_data():
    total = 0
    for i in range(1000000):
        total += i
    return total


process_data()
Question 07

Which scenarios are good candidates for asyncio?

HARD
  • A Handling thousands of concurrent API requests
  • B Heavy numerical matrix computation
  • C WebSocket communication
  • D Database polling with async drivers

Asyncio is optimized for high-concurrency I/O workloads where tasks spend significant time waiting. Examples include API gateways, chat systems, notification services, and real-time streaming applications.

CPU-intensive numerical processing is generally not a strong use case for asyncio because those operations block the event loop. Such workloads are typically delegated to multiprocessing, native extensions, or specialized libraries.

Question 08

Why are context managers important in production Python applications?

MEDIUM

Context managers ensure resources are cleaned up reliably even when exceptions occur. This is critical for files, database connections, network sockets, locks, and cloud SDK clients. Without proper cleanup, systems can leak memory, exhaust file descriptors, or leave transactions open.

The with statement improves readability because acquisition and cleanup logic stay together. It also reduces repetitive try-finally blocks that clutter application code.

In enterprise systems, custom context managers are often implemented for transaction handling, distributed locks, tracing spans, or temporary configuration overrides.

Question 09

Write a custom context manager for database transaction handling.

MEDIUM

This pattern ensures transactions are either committed or rolled back automatically. It reduces the risk of partially completed operations when exceptions occur during database updates.

Financial systems, healthcare applications, and inventory platforms commonly use similar patterns because transactional consistency is critical in those domains.

# Python
class TransactionManager:
    def __init__(self, connection):
        self.connection = connection

    def __enter__(self):
        self.cursor = self.connection.cursor()
        return self.cursor

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type:
            self.connection.rollback()
            print('Transaction rolled back')
        else:
            self.connection.commit()
            print('Transaction committed')

        self.cursor.close()


# Example usage
# with TransactionManager(connection) as cursor:
#     cursor.execute('INSERT INTO users VALUES (1, "Alice")')
Question 10

Which statements about Python lists and tuples are correct?

MEDIUM
  • A Tuples are immutable.
  • B Lists consume less memory than tuples in most cases.
  • C Tuples can be used as dictionary keys if they contain hashable items.
  • D Lists provide faster append operations than tuples.

Tuples are immutable and typically more memory efficient than lists because they have fewer dynamic features. That makes them suitable for fixed collections such as coordinate pairs or configuration mappings.

Lists are optimized for dynamic modification and resizing. In real systems, choosing between them depends on mutability requirements and performance characteristics.

Question 11

Write an asynchronous function that fetches data from multiple APIs concurrently.

HARD

Async concurrency significantly improves throughput when applications spend time waiting for remote services. API aggregators, notification systems, and monitoring platforms frequently use this model.

The key advantage is scalability. Instead of blocking threads for each network request, the event loop switches between tasks efficiently while waiting for responses.

# Python
import asyncio
import aiohttp


async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()


async def fetch_all(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        return await asyncio.gather(*tasks)


urls = [
    'https://example.com',
    'https://example.org'
]


# asyncio.run(fetch_all(urls))
Question 12

How would you debug memory leaks in a long-running Python service?

HARD

The first step is identifying whether memory growth is expected or abnormal. Monitoring tools such as tracemalloc, objgraph, memory_profiler, and Prometheus metrics help track object allocation patterns over time.

Common causes include global caches that never expire, circular references, unclosed resources, large retained data structures, or background tasks accumulating state. Logging memory snapshots periodically can reveal where growth occurs.

In production environments, engineers often combine heap analysis with load testing. Comparing memory profiles before and after sustained traffic helps isolate leaks that only appear under realistic workloads.

Question 13

Which built-in Python features support iteration protocols?

EASY
  • A __iter__
  • B __next__
  • C __call__
  • D yield

Iteration in Python relies on the iterator protocol, mainly through __iter__ and __next__. The yield keyword simplifies iterator creation by automatically managing state.

Understanding iterators becomes important when implementing streaming systems, paginated APIs, or memory-efficient pipelines.

Question 14

Write a generator that reads a large log file and filters ERROR entries.

MEDIUM

This generator avoids loading the full log file into memory, which is essential when processing gigabyte-scale application logs.

Operational monitoring systems often rely on similar streaming approaches for anomaly detection, alerting, and security event analysis.

# Python

def error_logs(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            if 'ERROR' in line:
                yield line.strip()


for log in error_logs('application.log'):
    print(log)
Question 15

Why are Python type hints valuable even though Python is dynamically typed?

MEDIUM

Type hints improve maintainability by making contracts between functions explicit. In large codebases with multiple teams, understanding expected input and output types reduces integration mistakes and improves readability.

Static analysis tools such as mypy and Pyright can detect type-related bugs before runtime. This becomes especially useful in backend APIs, financial systems, and ETL platforms where invalid data types may silently propagate.

Type hints also improve IDE support through autocomplete, refactoring assistance, and documentation generation. Many organizations adopt them gradually to improve long-term code quality without sacrificing Python's flexibility.

Question 16

Which practices improve Python application performance?

MEDIUM
  • A Using generators for large datasets
  • B Avoiding unnecessary object creation
  • C Replacing all loops with recursion
  • D Using built-in libraries implemented in C

Python performance tuning usually focuses on reducing memory allocations, minimizing interpreted loops, and leveraging optimized built-in libraries.

Recursion is not automatically faster in Python and may actually worsen performance due to stack overhead and recursion depth limits.

Question 17

Implement a thread-safe singleton pattern in Python.

HARD

Thread-safe singleton implementations are useful for centralized configuration management, connection pools, logging infrastructure, or cache managers.

Without locking, multiple threads may create duplicate instances simultaneously during initialization under high concurrency.

# Python
import threading


class SingletonMeta(type):
    _instances = {}
    _lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        with cls._lock:
            if cls not in cls._instances:
                cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class ConfigManager(metaclass=SingletonMeta):
    pass


obj1 = ConfigManager()
obj2 = ConfigManager()

print(obj1 is obj2)
Question 18

What are common mistakes when handling exceptions in enterprise Python applications?

HARD

One common mistake is catching broad exceptions without proper logging or re-raising. This hides root causes and makes debugging production failures significantly harder.

Another issue is using exceptions for normal control flow. Excessive exception handling in performance-critical sections can reduce readability and introduce unnecessary overhead.

Production-grade applications usually implement structured exception hierarchies, centralized logging, retry policies, and contextual error messages to improve observability and troubleshooting.

Question 19

Write a Python function that retries an API call with exponential backoff.

MEDIUM

Retry strategies are common in distributed systems because network failures and temporary service outages are unavoidable in real environments.

Exponential backoff prevents aggressive retry storms that could overload already struggling services. Cloud SDKs and enterprise APIs often implement similar mechanisms internally.

# Python
import time
import requests


def fetch_with_retry(url, retries=3, delay=1):
    for attempt in range(retries):
        try:
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            return response.json()
        except requests.RequestException as error:
            print(f'Attempt {attempt + 1} failed: {error}')

            if attempt < retries - 1:
                time.sleep(delay)
                delay *= 2

    raise Exception('All retry attempts failed')
Question 20

Which statements about Python decorators are true?

HARD
  • A Decorators can modify function behavior dynamically.
  • B Decorators are only useful for web frameworks.
  • C Decorators can preserve metadata using functools.wraps.
  • D Multiple decorators execute from bottom to top.

Decorators are widely used beyond web frameworks. Common use cases include metrics collection, tracing, access control, validation, caching, retries, and auditing.

Decorator order matters because wrapping occurs from the innermost decorator outward. Misunderstanding execution order can produce subtle production bugs.

Question 21

Write a function to remove duplicate items from a list while preserving order.

EASY

This approach preserves insertion order while maintaining efficient lookup performance through a set.

Deduplication logic frequently appears in data ingestion pipelines, reporting systems, and API normalization layers.

# Python

def remove_duplicates(items):
    seen = set()
    result = []

    for item in items:
        if item not in seen:
            seen.add(item)
            result.append(item)

    return result


print(remove_duplicates([1, 2, 2, 3, 1, 4]))
Question 22

How does Python garbage collection work alongside reference counting?

MEDIUM

Python primarily manages memory through reference counting. When an object's reference count reaches zero, memory is immediately reclaimed. This mechanism provides predictable cleanup for most objects.

However, circular references create situations where objects reference each other indefinitely. Python's cyclic garbage collector detects and removes these unreachable object groups periodically.

Understanding both systems becomes important in long-running services because poorly designed object graphs or unmanaged references can contribute to memory retention problems.

Question 23

Create a simple LRU cache implementation using OrderedDict.

HARD

LRU caches are commonly used to reduce expensive database queries, API calls, or computational overhead by storing recently accessed results.

Evicting the least recently used item helps maintain bounded memory usage while retaining frequently accessed data.

# Python
from collections import OrderedDict


class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = OrderedDict()

    def get(self, key):
        if key not in self.cache:
            return None

        self.cache.move_to_end(key)
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)

        self.cache[key] = value

        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)


cache = LRUCache(2)
cache.put('a', 1)
cache.put('b', 2)
cache.get('a')
cache.put('c', 3)
print(cache.cache)
Question 24

Which statements about Python virtual environments are correct?

MEDIUM
  • A Virtual environments isolate dependencies between projects.
  • B A virtual environment changes the Python language syntax.
  • C They help avoid dependency version conflicts.
  • D They are unnecessary in production systems.

Virtual environments are fundamental for dependency isolation. Different projects often require incompatible package versions, and environments prevent those conflicts.

They are widely used in development, CI/CD pipelines, and production deployments to ensure reproducible builds.

Question 25

Write a Python class using dataclasses for an employee record.

MEDIUM

Dataclasses reduce boilerplate code for models primarily used to store structured data. They automatically generate constructors, repr methods, and comparison operations.

This feature is widely used in API schemas, configuration models, DTOs, and lightweight domain objects.

# Python
from dataclasses import dataclass


@dataclass
class Employee:
    employee_id: int
    name: str
    department: str
    salary: float


employee = Employee(101, 'Alice', 'Engineering', 95000)
print(employee)
Question 26

What factors would you consider when designing a scalable Python REST API?

HARD

Scalability starts with efficient request handling, connection pooling, caching strategies, and database query optimization. Poor indexing or excessive serialization overhead can become bottlenecks long before CPU utilization becomes a problem.

Concurrency strategy is also important. Async frameworks may improve throughput for I/O-heavy workloads, while CPU-intensive tasks should be delegated to background workers or separate processing services.

Operational concerns matter equally. Structured logging, distributed tracing, metrics collection, rate limiting, retries, and circuit breakers are essential for maintaining stability under production traffic.

Question 27

Write a multiprocessing example that calculates squares in parallel.

HARD

Multiprocessing distributes CPU-intensive tasks across multiple processes, allowing true parallel execution on multi-core systems.

This model is frequently used in analytics engines, media processing pipelines, and scientific computing workloads.

# Python
from multiprocessing import Pool


def square(number):
    return number * number


if __name__ == '__main__':
    numbers = [1, 2, 3, 4, 5]

    with Pool(processes=4) as pool:
        results = pool.map(square, numbers)

    print(results)
Question 28

Which statements about Python sets are correct?

EASY
  • A Sets automatically remove duplicates.
  • B Sets maintain insertion order in all Python versions.
  • C Sets support fast membership checks.
  • D Sets allow mutable elements like lists.

Sets are optimized for uniqueness and fast membership testing. They are commonly used for deduplication, filtering, and lookup operations.

Set elements must be hashable, which means mutable types like lists cannot be added directly.

Question 29

Write a function that groups a list of dictionaries by a specific key.

MEDIUM

Grouping operations are common in analytics pipelines, reporting systems, and API aggregation logic.

Using defaultdict simplifies accumulation logic and improves readability compared to manual existence checks.

# Python
from collections import defaultdict



def group_by(items, key):
    grouped = defaultdict(list)

    for item in items:
        grouped[item[key]].append(item)

    return dict(grouped)


employees = [
    {'name': 'Alice', 'dept': 'IT'},
    {'name': 'Bob', 'dept': 'HR'},
    {'name': 'Charlie', 'dept': 'IT'}
]


print(group_by(employees, 'dept'))
Question 30

Why is automated testing important in Python projects with rapid release cycles?

MEDIUM

Automated testing reduces regression risk when teams deploy frequently. In systems with multiple contributors, small code changes can unintentionally break unrelated functionality without adequate test coverage.

Unit tests validate isolated logic, integration tests verify component interactions, and end-to-end tests simulate real user behavior. Combining these layers provides stronger confidence during deployments.

Modern CI/CD pipelines rely heavily on automated tests to maintain release velocity. Without them, teams often slow down deployments because manual verification becomes too expensive and unreliable.