PythonPandas.com

Accenture Python Interview Questions and Answers



We have curated a list of all Python Interview Questions which are recently asked in Accenture company. These Accenture Python Interview Questions consist interview question with detailed answers.

1. What is Python? What are its key features?

Answer:
Python is a high-level, interpreted, general-purpose programming language created by Guido van Rossum. Key features include:

  • Readability and simplicity (clean syntax)

  • Dynamically typed

  • Interpreted (executes bytecode via Python VM)

  • Large standard library (“batteries included”)

  • Support for multiple paradigms: procedural, OOP, functional

  • Extensible (can interface with C/C++), embeddable

  • Strong community and ecosystem (packages via PyPI)

2. What is PEP 8 and why is it important?

Answer:
PEP 8 is the Python Enhancement Proposal #8, a style guide for writing idiomatic, readable Python code. It prescribes naming conventions, indentation, line length, whitespace rules, imports order, etc. Following PEP 8 makes code more maintainable and comprehensible by teams.

3. What’s the difference between is and == in Python?

Answer:

  • == checks for value equality (i.e. whether the objects have equal content, via __eq__)

  • is checks for identity, i.e. whether they refer to the same object in memory

Example:

a = [1,2,3]
b = [1,2,3]
a == b # True
a is b # False
c = a
c is a # True

4. Explain how memory management works in Python.

Answer:
Python uses automatic memory management via:

  1. Reference counting — every object tracks how many references point to it; when count drops to zero, it’s deallocated

  2. Garbage collector — handles cyclic references (objects referring to each other) by periodically detecting unreachable cycles and freeing them

  3. Private heap — all Python objects and data live in Python’s private heap, managed by the interpreter

  4. Memory pools / arenas — for small objects, Python uses memory pooling to reduce fragmentation

5. What are mutable and immutable types? Give examples.

Answer:

  • Immutable types: their content cannot change after creation. Examples: int, float, str, tuple, frozenset.

  • Mutable types: can be changed in-place. Examples: list, dict, set, bytearray.

Because immutable objects cannot change, operations that “modify” them actually produce new objects (e.g. string concatenation).


6. Explain shallow copy vs deep copy.

Answer:

  • Shallow copy: copies the container object, but references to nested (inner) objects are shared. So changes in nested mutable objects reflect in both copies.

  • Deep copy: recursively copies all nested objects, so the copy is fully independent.

Python has a copy module:

import copy
a = [[1,2], [3,4]]
b = copy.copy(a) # shallow
c = copy.deepcopy(a) # deep

7. What is a decorator? Give an example.

Answer:
A decorator is a higher-order function that takes a function and returns a modified function (or wraps behavior around it) without modifying its code. They are often used for logging, access control, caching, instrumentation, etc.

Example:


def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before call")
result = func(*args, **kwargs)
print("After call")
return result
return wrapper

@my_decorator
def say_hello(name):
print(f”Hello, {name}”)

say_hello(“Alice”)

8. What is the difference between @staticmethod and @classmethod?

Answer:

  • @staticmethod: a method that doesn’t receive an implicit first argument (no self or cls). It behaves like a plain function placed inside a class for organizational reason.

  • @classmethod: receives the class (cls) as the first argument, and can access/modify class state.

Example:

class MyClass:
@staticmethod
def static_method(x, y):
return x + y

@classmethod
def class_method(cls, val):
return cls(val)

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


9. What is a generator and how is it different from a normal function?

Answer:
A generator is a function that yields values one at a time, pausing its state between yields, when you call next() on it. It is memory-efficient for large data sequences because it doesn’t build the entire list in memory.

Differences:

  • Use yield instead of return.

  • Generators implement the iterator protocol (__iter__ and __next__).

  • They resume from last yield point, maintaining local state.

Example:

def my_gen(n):
for i in range(n):
yield i * 2

g = my_gen(5)
next(g) # 0
next(g) # 2


10. How do *args and **kwargs work in function definitions?

Answer:

  • *args collects extra positional arguments as a tuple.

  • **kwargs collects extra keyword arguments as a dict.

Example:

def f(a, b, *args, **kwargs):
print(a, b)
print(args)
print(kwargs)

f(1, 2, 3, 4, x=10, y=20)
# Output:
# 1 2
# (3, 4)
# {‘x’: 10, ‘y’: 20}

They’re useful for flexible APIs and forwarding arguments.


11. What is the Global Interpreter Lock (GIL) in CPython? How does it affect multi-threading?

Answer:
GIL is a mutex in CPython that ensures only one thread executes Python bytecode at a time. This means that multi-threading in Python is less effective for CPU-bound tasks (they often run serially). However, for I/O-bound tasks (network, file I/O), threads can still be useful because while one thread waits on I/O, another can run.

To bypass GIL for CPU-bound work, you can use:

  • Multiprocessing (separate processes)

  • Use external libraries (C extensions) that release GIL

  • Use async/await / event-driven concurrency


12. Explain Python’s exception handling (try / except / else / finally).

Answer:

  • try: block to wrap code that might raise exceptions

  • except: catches and handles specified exception(s)

  • else: executes if no exception was raised in try

  • finally: always executes, whether exception occurred or not (useful for cleanup)

Example:

try:
x = 1 / 0
except ZeroDivisionError as e:
print("Divide by zero error:", e)
else:
print("No error")
finally:
print("Cleanup code runs")

13. What is monkey patching in Python?

Answer:
Monkey patching refers to dynamically modifying or extending code at runtime (e.g. overriding methods or attributes) of existing modules or classes without altering the source. It is sometimes used for testing or hotfixes, but should be used cautiously.

Example:

import math

def new_sqrt(x):
return “patched!”

math.sqrt = new_sqrt

print(math.sqrt(9)) # “patched!”


14. What is name mangling in Python? When is it used?

Answer:
Name mangling is how Python deals with class private attributes. If a class attribute name starts with at least two underscores (and does not end with two underscores), Python rewrites it internally to include the class name, e.g., __var becomes _ClassName__var. This helps avoid name collisions in subclasses.

Used for “pseudo private” to avoid accidental override, not true privacy.


15. Why does Python not support method overloading like Java/C++?

Answer:
In Python, if you define multiple methods with the same name but different signatures (parameters), the last definition overrides previous ones. Python uses duck typing rather than static overloading. Instead, you can use default arguments, variable arguments (*args, **kwargs), or single dispatch (functools) to mimic overloading behavior.


16. What is a context manager and the with statement? How do you implement one?

Answer:
A context manager is a protocol with __enter__ and __exit__ methods used to encapsulate setup and teardown logic (e.g. opening/closing files). The with statement invokes these automatically.

Example:

class MyContext:
def __enter__(self):
print("Entering")
return self

def __exit__(self, exc_type, exc_val, exc_tb):
print(“Exiting”)

with MyContext() as ctx:
print(“Inside context”)

Or use contextlib to simplify.


17. Explain how to create and use virtual environments in Python.

Answer:
Virtual environments isolate dependencies per project rather than installing globally.

  • To create: python3 -m venv envname

  • To activate:
      On Linux/macOS: source envname/bin/activate
      On Windows: envname\Scripts\activate

  • To deactivate: deactivate

  • You can also use tools like virtualenv, pipenv, poetry.

Within a virtual environment, pip install installs packages to the project scope only.


18. What is the difference between list.sort() and sorted()?

Answer:

  • list.sort() is an in-place method: it modifies the original list and returns None.

  • sorted() is a built-in function: it returns a new sorted list, leaving the original untouched.

Both accept arguments like key and reverse.


19. How can you reverse a string in Python?

Answer (multiple ways):

  1. Slicing: s[::-1]

  2. Using reversed() and join: ''.join(reversed(s))

  3. Looping or stack (less common)

Example:

s = "hello"
rev = s[::-1] # "olleh"

20. Write a function to check if a string is a palindrome (ignoring spaces, punctuation, case).

Answer (sample):

import re

def is_palindrome(s: str) -> bool:
cleaned = re.sub(r'[^A-Za-z0-9]’, , s).lower()
return cleaned == cleaned[::-1]

# Examples:
print(is_palindrome(“A man, a plan, a canal: Panama”)) # True


21. What is a lambda function? Give an example where it’s used.

Answer:
A lambda is an anonymous single-expression function defined with the lambda keyword.

Example:

add = lambda x, y: x + y
print(add(3, 4)) # 7

# Or inline usage:
squares = list(map(lambda x: x*x, [1, 2, 3, 4]))

Often used for short callback functions (e.g. key functions in sort, map/filter).


22. Explain what list comprehensions are and give an example.

Answer:
List comprehensions provide a concise way to create lists. They typically follow the form:

[expression for item in iterable if condition]

Example:

evens = [x for x in range(20) if x % 2 == 0]

This is more readable and often faster than equivalent loops.


23. How do you sort a dictionary by value, not by key?

Answer:

d = {'a':5, 'b':2, 'c':9}
sorted_items = sorted(d.items(), key=lambda kv: kv[1])
# Returns a list of (key, value) tuples sorted by value
# If you need a dictionary again:
sorted_dict = {k: v for k, v in sorted_items}

Alternatively, using operator.itemgetter:

from operator import itemgetter
sorted(d.items(), key=itemgetter(1))

24. Explain how to iterate over two lists in parallel.

Answer:
Use the zip() function:

list1 = [1,2,3]
list2 = ['a','b','c']

for x, y in zip(list1, list2):
print(x, y)

If the lengths differ, zip stops at the shortest. Use itertools.zip_longest to continue with a fill value.


25. What are Python modules and packages?

Answer:

  • A module is a .py file containing Python definitions (functions, classes, variables) that you can import.

  • A package is a directory containing modules and a special __init__.py file, helping structure modules in nested namespace hierarchies.

You import modules with import module or from module import X.


26. What is dynamic typing? How does it differ from static typing?

Answer:
Dynamic typing means the type of variable is determined at runtime, not declared explicitly. Variables can refer to objects of any type, and the same variable can be reassigned to different types.

Static typing (e.g. in Java, C++) requires declaring variable types at compile time and enforces type constraints.

Example:

x = 5 # x is int
x = "hello" # now x is str

27. Explain the difference between append() vs extend() in lists.

Answer:

  • append(x) adds one element (object x) to the end of the list.

  • extend(iterable) iterates over the iterable and appends each element to the list.

Example:

a = [1,2]
a.append([3,4]) # [1,2,[3,4]]
a = [1,2]
a.extend([3,4]) # [1,2,3,4]

28. How do you handle missing keys in a dictionary (i.e. default values)?

Answer:

  • Use dict.get(key, default) which returns default if key not present

  • Use collections.defaultdict for automatic default values

  • Use setdefault(key, default) to insert default if not present

Example:

from collections import defaultdict
d = defaultdict(int)
print(d['x']) # 0

29. What is the difference between filter(), map(), and reduce()?

Answer:

  • map(func, iterable) applies func to each element and returns an iterator of results

  • filter(func, iterable) returns elements for which func(element) is True

  • reduce(func, iterable) (from functools) reduces the iterable to a single value by cumulatively applying func

Example:

from functools import reduce

nums = [1,2,3,4]
doubled = list(map(lambda x: x*2, nums)) # [2,4,6,8]
evens = list(filter(lambda x: x%2==0, nums)) # [2,4]
sum_all = reduce(lambda a,b: a+b, nums) # 10


30. How would you remove duplicates from a list while maintaining order?

Answer:

One approach:

def dedupe(seq):
seen = set()
result = []
for x in seq:
if x not in seen:
seen.add(x)
result.append(x)
return result

Alternatively, in Python 3.7+ (dict preserves insertion order):

list(dict.fromkeys(my_list))

31. What are Python’s built-in data types?

Answer:

Common data types include:

  • Numeric: int, float, complex

  • Sequence: list, tuple, range

  • Text: str

  • Mapping: dict

  • Set types: set, frozenset

  • Boolean: bool

  • Binary: bytes, bytearray, memoryview

  • NoneType: None


32. What is the difference between __str__ and __repr__ methods?

Answer:

  • __repr__ is meant to represent the object in an “official” way, ideally so that eval(repr(obj)) == obj. It is unambiguous.

  • __str__ is meant to be readable (user-facing) string representation.

If you only implement one, fallback is:

  • __repr__, if __str__ not defined

Example:

class Point:
def __init__(self, x, y):
self.x = x; self.y = y

def __repr__(self):
return f”Point({self.x}, {self.y})”

def __str__(self):
return f”({self.x}, {self.y})”

p = Point(1,2)
print(p) # calls __str__, prints “(1, 2)”
p # in REPL calls __repr__, prints “Point(1, 2)”


33. Write code to flatten a nested list, e.g. [[1,2], [3, [4,5]], 6].

Answer (recursive):

def flatten(lst):
for elem in lst:
if isinstance(elem, (list, tuple)):
yield from flatten(elem)
else:
yield elem

nested = [[1,2], [3, [4,5]], 6]
flat = list(flatten(nested))
print(flat) # [1,2,3,4,5,6]


34. What are metaclasses in Python?

Answer:
A metaclass is the “class of a class”; it’s the template that Python uses to create classes. You can customize class creation by defining a metaclass (via metaclass= in class definition). It allows controlling how classes behave, enforce patterns, modify class attributes, etc.

Example:

class MyMeta(type):
def __new__(mcs, name, bases, namespace):
namespace['added'] = lambda self: "Added method"
return super().__new__(mcs, name, bases, namespace)

class MyClass(metaclass=MyMeta):
pass

obj = MyClass()
print(obj.added()) # “Added method”


35. What is monkey patching and when is it used?

(This is similar to question 13; intentionally repeating to test deeper understanding in interview)

Answer:
Monkey patching is dynamically modifying or extending classes or modules at runtime (after import). Use cases include:

  • Patching behavior for testing (mocking)

  • Hotfixing bugs without modifying original code

  • Hooking into external library behavior

Be careful: can lead to maintenance headaches or unexpected side effects.


36. Explain how you’d build a simple REST API in Python (e.g. using Flask). What should you consider for security and scalability?

Answer:

Steps and considerations:

  1. Use a lightweight framework like Flask or FastAPI

  2. Define endpoints (@app.route) with methods (GET, POST, PUT, DELETE)

  3. Use blueprints / routers to modularize

  4. Input validation and sanitization (e.g. using pydantic, marshmallow)

  5. Use authentication/authorization (JWT, OAuth, API keys)

  6. Handle error/exception centrally

  7. Use pagination for large data sets

  8. Use caching (Redis, in-memory)

  9. Use rate limiting (prevent abuse)

  10. Logging, monitoring, metrics

  11. Use WSGI servers (Gunicorn, uWSGI) behind reverse proxy (nginx)

  12. Use database connection pooling

  13. Enable HTTPS / TLS, secure headers

  14. Load balancing, horizontal scaling, stateless architecture (store state outside)

A candidate may be asked to sketch the API, show code, or discuss trade-offs.


37. What’s the difference between multiprocessing and multithreading? When would you use one or the other in Python?

Answer:

  • Multithreading: multiple threads in the same process, share memory. Useful for I/O-bound tasks. But due to GIL, CPU-bound threads are ineffective in CPython.

  • Multiprocessing: separate processes, separate memory space. Bypasses GIL, so good for CPU-bound tasks.

Use multithreading for I/O (network calls, file reads). Use multiprocessing for CPU-heavy tasks (math, data crunching).


38. What are namedtuple, deque, defaultdict, Counter in the collections module? Give use-cases.

Answer:

  • namedtuple: factory for tuple subclasses with named fields (accessible by name and index).
    Use-case: lightweight immutable record (e.g. Point = namedtuple(“Point”, [“x”,”y”]))

  • deque: double-ended queue with fast appends/pops from both ends.
    Use-case: queue / stack, sliding windows

  • defaultdict: dict that provides default values for missing keys (via a default factory).

  • Counter: subclass of dict for counting hashable objects; useful to tally frequencies


39. What is method resolution order (MRO) in Python? How does it work in multiple inheritance?

Answer:
MRO is the order Python uses to look up methods/attributes in classes. Python 3 uses C3 linearization (C3 superclass linearization) for MRO. In multiple inheritance, it ensures a consistent order that respects class hierarchy and preserves monotonicity.

You can inspect ClassName.__mro__ or ClassName.mro() to see the order.

Example:

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

print(D.__mro__)


40. Explain how garbage collection of cyclic references works.

Answer:
Python’s GC module periodically runs a cyclic garbage collector which:

  1. Identifies groups of objects that reference each other but are unreachable from root references

  2. Breaks cycles and deallocates them

  3. Uses generation-based collection: young, mid, old generations; more frequent collection in younger generations

You can interact via the gc module (e.g. gc.collect(), gc.get_objects()).


41. Describe how you would profile and optimize a slow Python application.

Answer:

  • Use profiling tools: cProfile, profile, line_profiler, pyinstrument

  • Identify hotspots (functions consuming most time)

  • Optimize algorithms or data structures (e.g. change complexity)

  • Use builtins or optimized libraries (NumPy, C-extensions)

  • Avoid repeated work, caching results (memoization)

  • Use concurrency / parallelism (multiprocessing, async)

  • Use just-in-time compilers (e.g. Numba) or alternative implementations (PyPy)

  • Reduce I/O bottlenecks (buffer, batching)

  • Use efficient data formats, avoid excessive copying


42. How would you implement a thread-safe singleton in Python?

Answer:

One approach using a metaclass with locking:

import threading

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

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

class MySingleton(metaclass=SingletonMeta):
pass

This ensures only one instance is created, even under concurrency.


43. How do you merge two sorted lists into one sorted list (without using built-ins like sorted)?

Answer (merge step of merge sort):

def merge(a, b):
i, j = 0, 0
merged = []
while i < len(a) and j < len(b):
if a[i] <= b[j]:
merged.append(a[i]); i += 1
else:
merged.append(b[j]); j += 1
merged.extend(a[i:])
merged.extend(b[j:])
return merged

print(merge([1,3,5], [2,4,6])) # [1,2,3,4,5,6]


44. Suppose you have a list of integers, find the pair whose sum is closest to zero (or a target). How would you approach?

Answer (two-pointer approach after sorting):

def pair_closest_to_zero(arr):
arr.sort()
left, right = 0, len(arr)-1
best = (None, None)
best_sum = float('inf')
while left < right:
s = arr[left] + arr[right]
if abs(s) < abs(best_sum):
best_sum = s
best = (arr[left], arr[right])
if s < 0:
left += 1
else:
right -= 1
return best, best_sum

Talk through edge-cases (duplicates, size < 2).


45. What is the output and reasoning of the following snippet?

def f(a, b, L=[]):
L.append(a * b)
return L

print(f(2, 3))
print(f(3, 4))
print(f(4, 5, []))
print(f(5, 6))

Answer:
Output:

[6]
[6, 12]
[20]
[6, 12, 30]

Reason: The default list L=[] is evaluated once at function definition. So calls without providing L reuse the same list. The third call passes a new empty list, so it doesn’t use the default. Hence the pattern.


46. What will be printed by this code?

class A:
def __init__(self):
self.x = 1

class B(A):
def __init__(self):
super().__init__()
self.y = 2

obj = B()
print(obj.x, obj.y)

Answer:
It prints:

1 2

Because B.__init__() calls super().__init__() (the initializer of A) which sets x = 1, then B sets y = 2.


47. How do you handle JSON in Python? Convert a Python dict to JSON string and vice versa.

Answer:

Use the json module:

import json

d = {‘name’: ‘Alice’, ‘age’: 30}
s = json.dumps(d) # dict → JSON string
print(s) # e.g. ‘{“name”: “Alice”, “age”: 30}’

d2 = json.loads(s) # JSON string → dict
print(d2[‘name’]) # “Alice”

You can also use json.dump() / json.load() to work directly with files.


48. What is pickling / unpickling in Python?

Answer:

  • Pickling is serializing a Python object into a byte stream.

  • Unpickling is deserializing that byte stream back into a Python object.

You use the pickle module:

import pickle
data = {'a':1, 'b':2}
b = pickle.dumps(data) # to bytes
d = pickle.loads(b) # back to dict

Be careful: unpickling untrusted data can be unsafe (arbitrary code execution).


49. Write a function to detect if a linked list has a cycle (Floyd’s Tortoise and Hare algorithm).

Answer (pseudo / Python):

class Node:
def __init__(self, val):
self.val = val
self.next = None

def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False

Explanation: The fast pointer moves twice as fast as slow. If there is a cycle, they will eventually meet.


50. How would you design an LRU cache in Python? (Implementation sketch)

Answer:

You can implement LRU (least recently used) cache using a combination of:

  • A doubly linked list (for O(1) insert/delete)

  • A hash map/dictionary mapping keys to nodes

Alternatively, use collections.OrderedDict (Python 3.7+ dict is ordered too) to simplify:

from collections import OrderedDict

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

def get(self, key):
if key not in self.cache:
return1
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) # remove least recently used

This gives O(1) average get and put performance.

51. What is the difference between __new__ and __init__?

Answer:

  • __new__(cls, *args, **kwargs) is a static (or class) method that actually creates and returns a new instance (i.e., it allocates memory). It’s called before __init__.

  • __init__(self, *args, **kwargs) initializes the already created instance (sets up attributes, etc.).

If you override __new__, you must return an instance (usually via super().__new__(cls)).


52. What is the __slots__ mechanism and when would you use it?

Answer:
__slots__ allows you to declare a fixed set of attributes for a class, preventing the creation of a per-instance __dict__. This can reduce memory usage and improve attribute access speed, especially if you have many instances.

Example:

class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y

But using __slots__ disallows dynamically adding new attributes and complicates multiple inheritance.


53. How does Python’s method resolution order (MRO) handle diamond inheritance (the “diamond problem”)?

Answer:
Python uses the C3 linearization algorithm for MRO (in Python 3). In the diamond inheritance pattern (A → B and A → C, then D inherits B and C), the MRO ensures each class appears only once and in a consistent “merge” order.

For example:

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

print(D.mro()) # => [D, B, C, A, object]

The order ensures that you don’t visit A twice, and respects the inheritance hierarchy.


54. Explain how Python’s weakref module works and give use cases.

Answer:
The weakref module provides “weak references” to objects, which do not increase the object’s reference count. When the object is garbage-collected (i.e. no strong references remain), the weak reference becomes invalid (calls a callback or yields None).

Use cases:

  • Caches or memoization where you don’t want the cache to prevent object destruction

  • Observer patterns

  • Avoiding memory leaks in circular references or registries

Example:

import weakref

class MyClass:
pass

obj = MyClass()
wr = weakref.ref(obj)
print(wr()) # returns obj

del obj
print(wr()) # returns None


55. What are coroutines in Python (beyond just generators)? How do async / await work?

Answer:
Coroutines are functions that can suspend and resume execution, enabling asynchronous programming. In Python:

  • async def foo(): defines a coroutine

  • await is used to pause execution until an awaitable (another coroutine, asyncio future, etc.) completes

  • They allow you to write non-blocking code in a sequential style

Under the hood, asyncio or other event loops schedule and run coroutines. Coroutines enable high concurrency especially for I/O-bound tasks.


56. What is the difference between asyncio.Task and asyncio.Future?

Answer:

  • A Future is a low-level object representing an eventual result (placeholder) of an asynchronous operation.

  • A Task is a subclass of Future that wraps a coroutine and schedules its execution.

When you call asyncio.create_task(coro), you get a Task, which is also a Future, but it automatically starts running the coroutine in the event loop.


57. How would you limit the rate of requests (rate limiting) in a Python web service?

Answer:
Some strategies:

  • Use a leaky bucket or token bucket algorithm (maintain tokens, refill periodically)

  • Use a middleware or decorator that tracks request timestamps per user/IP and rejects beyond thresholds

  • Use an external store (Redis) to count requests and TTL keys

  • Combine with caching and throttling libraries (e.g. ratelimit, limits)

  • Enforce rate limits at the API gateway / reverse proxy (e.g. nginx)

Code sketch:

import time
from functools import wraps

requests = {} # mapping key → [timestamps]

def rate_limit(key_fn, period, max_calls):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
key = key_fn(*args, **kwargs)
now = time.time()
timestamps = requests.get(key, [])
# drop old
timestamps = [t for t in timestamps if t > now – period]
if len(timestamps) >= max_calls:
raise Exception(“Too many requests”)
timestamps.append(now)
requests[key] = timestamps
return fn(*args, **kwargs)
return wrapper
return decorator


58. Explain how the Python Global Interpreter Lock (GIL) affects multiprocessing and threading.

Answer:

  • Threading: Because of the GIL, only one thread executes Python bytecode at a time in CPython. So CPU-bound tasks do not gain parallelism; only I/O-bound tasks benefit.

  • Multiprocessing: Each process has its own Python interpreter and memory space, so each has its own GIL. Thus, multiprocessing can bypass the GIL and achieve parallelism for CPU-bound tasks.

This is why for CPU-intensive workloads, multiprocessing, or offloading to C extensions or other languages, is often used.


59. Explain closure in Python with an example. How is it useful?

Answer:
A closure occurs when an inner function “remembers” variables from its enclosing scope even after the outer function has returned.

Example:

def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier

times3 = make_multiplier(3)
print(times3(10)) # 30

Here multiplier is a closure capturing n. Useful for factories, partial application, callbacks or configuring behavior dynamically.


60. What is the functools.lru_cache decorator? How does it work?

Answer:
functools.lru_cache is a decorator that caches the results of function calls with given arguments (Least Recently Used cache). If the function is called again with the same args, the cached result is returned instead of recomputation.

It maintains a fixed-size cache and evicts least recently used items when full. Great for expensive recursive or pure functions.

Example:

from functools import lru_cache

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


61. How would you serialize Python objects to JSON where they contain non-serializable types (e.g. datetime, custom classes)?

Answer:
Approaches:

  • Provide a custom JSON encoder by subclassing json.JSONEncoder and override default(self, obj)

  • Provide a to_json or to_dict method in your class and manually convert

  • Use libraries like pydantic, marshmallow, or orjson which support custom types

  • Pre-convert the non-serializable fields (e.g. datetime.isoformat())

Example:

import json, datetime

class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
return super().default(obj)

d = {‘time’: datetime.datetime.now()}
json_str = json.dumps(d, cls=MyEncoder)


62. Describe Python’s descriptor protocol. What are descriptors and when are they used?

Answer:
A descriptor is an object attribute with “binding behavior,” i.e. implements any of __get__, __set__, __delete__. Descriptors let you customize attribute access.

  • If an attribute’s class defines __get__, __set__, or __delete__, it’s a descriptor.

  • property is a built-in descriptor.

  • Use cases: validation, type checking, lazy attributes, computed properties, ORM fields etc.

Example:

class Integer:
def __init__(self, name):
self.name = name

def __get__(self, instance, owner):
return instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError(“Expected int”)
instance.__dict__[self.name] = value

class Point:
x = Integer(‘x’)
y = Integer(‘y’)

p = Point()
p.x = 5
print(p.x)
p.x = “hello” # TypeError


63. How do you implement retry logic (with exponential backoff) in Python?

Answer:
You can write a decorator or helper function to wrap a function invocation with retry logic, optionally with exponential backoff and jitter.

Example:

import time
import random
from functools import wraps

def retry(max_attempts=3, backoff_factor=0.5, jitter=0.1):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
attempt = 1
while True:
try:
return fn(*args, **kwargs)
except Exception as e:
if attempt >= max_attempts:
raise
sleep = backoff_factor * (2 ** (attempt – 1))
sleep = sleep + random.uniform(0, jitter)
time.sleep(sleep)
attempt += 1
return wrapper
return decorator

@retry(max_attempts=4)
def unreliable_action():
# might throw


64. How would you implement a thread-safe queue/stack (or other data structure) in Python?

Answer:
You can use synchronization primitives from threading like Lock, RLock, Condition or use built-in thread-safe data structures:

  • queue.Queue, queue.LifoQueue, queue.PriorityQueue are thread-safe

  • If implementing custom, wrap internal data structure operations (push/pop) inside locks:

import threading

class ThreadSafeStack:
def __init__(self):
self._stack = []
self._lock = threading.Lock()

def push(self, x):
with self._lock:
self._stack.append(x)

def pop(self):
with self._lock:
return self._stack.pop()


65. Explain how Python’s bisect module works and give use cases.

Answer:
The bisect module provides functions to insert and search sorted lists, maintaining order in efficient time (binary search). Key functions:

  • bisect_left(a, x): find leftmost insertion point

  • bisect_right(a, x) or bisect(a, x): find rightmost insertion point

  • insort_left, insort_right: insert into list preserving sorted order

Use cases: maintaining sorted lists, scheduling, prediction, binary searches for thresholds.


66. Write a function to compute the longest increasing subsequence (LIS) of a list of numbers.

Answer (dynamic programming / patience sort):

Simplest (O(n²)):

def lis(nums):
if not nums:
return []
dp = [1] * len(nums)
prev = [-1] * len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i] and dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1
prev[i] = j
# find index of max dp
max_idx = max(range(len(nums)), key=lambda i: dp[i])
# reconstruct
seq = []
i = max_idx
while i != -1:
seq.append(nums[i])
i = prev[i]
return seq[::-1]

print(lis([10,9,2,5,3,7,101,18])) # e.g. [2,3,7,18]

Alternatively, O(n log n) algorithms exist using patience sorting technique.


67. How would you implement a publish-subscribe (pub/sub) system in Python?

Answer (simple in-memory version):

from collections import defaultdict

class PubSub:
def __init__(self):
self.subscribers = defaultdict(list)

def subscribe(self, topic, fn):
self.subscribers[topic].append(fn)

def publish(self, topic, data):
for fn in self.subscribers.get(topic, []):
fn(data)

# Example usage
ps = PubSub()
ps.subscribe(‘news’, lambda d: print(“Got news:”, d))
ps.publish(‘news’, “Python 4.0 released”)

For production, you’d use message brokers (RabbitMQ, Kafka), event loops, durable storage, topic filtering, etc.


68. What is method overloading / operator overloading in Python? How can you overload operators?

Answer:
Python doesn’t support method overloading by signature, but you can support different argument types in a single method (via *args, **kwargs) or use functools.singledispatch.

Operator overloading is done by defining “magic” methods in classes:

  • __add__, __sub__, __mul__, __eq__, etc.

  • __radd__, __iadd__ for reversed or in-place operations

Example:

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

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

def __repr__(self):
return f”Vec({self.x}, {self.y})”

print(Vec(1,2) + Vec(3,4)) # Vec(4,6)


69. Suppose you have a large log file too big to load entirely in memory. How would you count the most frequent ‘error codes’ in it?

Answer:

Approach:

  • Read line by line (streaming)

  • Use a collections.Counter or defaultdict(int) to tally counts

  • Optionally, use a fixed-size heap to track top-k frequent

  • If the dataset is too large to keep entire counts in memory, you can use:

  - Count-Min Sketch (approximate counters)
  - Streaming algorithms (e.g. heavy hitters)
  - Partition the file (map-reduce style)

Example:

from collections import Counter

counts = Counter()
with open(‘big.log’) as f:
for line in f:
code = parse_error_code(line)
counts += 1

top10 = counts.most_common(10)


70. How would you implement dependency injection in Python?

Answer:
Dependency injection (DI) means supplying components with their dependencies from the outside rather than creating internally. In Python, you can use:

  • Constructor injection: pass dependencies to __init__

  • Setter injection: set dependencies via setter methods

  • Use function parameters

  • Use a DI container / framework (e.g. injector, dependency_injector)

  • Use factories or provider functions

Example:

class DBClient:
def query(self, sql): pass

class Service:
def __init__(self, db: DBClient):
self.db = db

# At wiring time:
db = DBClient()
svc = Service(db)

This makes testing easier (you can inject mocks).


71. Explain how you’d design a task scheduler (cron-like) in Python.

Answer (high-level):
Components:

  1. Job registry (tasks, schedules, metadata)

  2. Scheduler loop / event loop

  3. Mechanism to compute next run times (cron syntax or fixed intervals)

  4. Worker execution (spawn threads, processes, or background tasks)

  5. Persistence (jobs survive restarts)

  6. Handling failures, retries, logging

  7. Concurrency control, job locking

  8. Time zones, daylight savings, drift

  9. API to add/remove tasks

You might use or be inspired by existing libraries like APScheduler, Celery beat, or croniter.


72. What are memoryviews and how can they help you optimize Python programs?

Answer:
memoryview is a built-in type that provides a “view” of the underlying buffer (e.g. bytes, bytearray, array) without copying data. You can manipulate slices and sub-buffers efficiently.

Use cases:

  • Avoid data copies when slicing large buffers

  • Work with binary data in networking or I/O

  • Zero-copy protocols

Example:

b = bytearray(b'abcdef')
mv = memoryview(b)
sub = mv[2:5]
sub[:] = b'XYZ' # no copy
print(b) # bytearray(b'abXYZf')

73. How can you dynamically load a module or class by name (i.e. string) at runtime?

Answer:
You can use:

  • importlib.import_module(module_name)

  • getattr(module, class_name)

  • Or __import__ + getattr

Example:

import importlib

module = importlib.import_module('mymodule.sub')
MyClass = getattr(module, 'MyClass')
obj = MyClass()

This is useful for plugin architectures, dependency injection, configuration-driven loading.


74. Explain what monkey patching is and a scenario where it's useful (but also its dangers).

Answer:
Monkey patching is dynamically modifying or overriding classes or modules at runtime.

Use-case:

  • In tests, you override methods or functions in third-party modules to simulate behavior

  • Hotfixing behavior without modifying installed library

Danger:

  • Can lead to unpredictable behavior or maintenance difficulty

  • Hidden side effects

  • Breaks assumptions, especially if library updates

Example:

import some_lib
def fake_func(...): ...
some_lib.func = fake_func

(You must ensure the new function signature matches expectations.)


75. Suppose you have a stream of data and you want to compute a running median (i.e. median of values seen so far). How would you implement that efficiently?

Answer:
You can maintain two heaps:

  • A max-heap for the lower half

  • A min-heap for the upper half

Balance them so that sizes differ at most by 1. The median is either the top of one heap (if odd count) or average of tops (if even count).

Example sketch:

import heapq

class RunningMedian:
def __init__(self):
self.low = [] # max-heap (store negatives)
self.high = [] # min-heap

def add(self, x):
if not self.low or x <= -self.low[0]:
heapq.heappush(self.low, -x)
else:
heapq.heappush(self.high, x)
# rebalance
if len(self.low) > len(self.high) + 1:
heapq.heappush(self.high, -heapq.heappop(self.low))
elif len(self.high) > len(self.low) + 1:
heapq.heappush(self.low, -heapq.heappop(self.high))

def median(self):
if len(self.low) > len(self.high):
return -self.low[0]
elif len(self.high) > len(self.low):
return self.high[0]
else:
return (-self.low[0] + self.high[0]) / 2

rm = RunningMedian()
for v in [5, 2, 10, 4, 3]:
rm.add(v)
print(rm.median())

This gives O(log n) per insertion and constant median retrieval.

Tips / Focus Areas for Accenture Python Interview

  • Be comfortable with data structures & algorithms (lists, sets, dicts, graphs, trees)

  • Practice coding patterns / problem-solving (two-sum, sliding window, merges, dynamic programming)

  • Know standard libraries well: itertools, collections, functools, heapq, bisect

  • Be able to optimize / analyze complexity

  • Be ready to talk about real projects: how you structured code, used Python packages, scaled systems

  • Practice writing clean, readable, testable code (Accenture emphasizes maintainability)

  • Understand concurrency, memory management, profiling

Related Post

Python SetPython Set

Python sets are a fundamental data structure used for storing unordered collections of unique elements. This article dives deep into Python sets, covering their creation, common operations like adding, removing,