☰

Python Interview Questions and answers

Top Python Interview Questions and Answers

Python is one of the most commonly tested languages in interviews for data analyst, data scientist, automation and backend roles. This guide collects 76 Python interview questions and answers, grouped by experience level, so you can work through them in the order a real interview usually follows. Every answer starts with a short, direct response you can say aloud, followed by detail where it helps.

Each code sample was run on Python 3.13 and the printed output is shown under it, so you can compare it with your own result. The page is written for current Python, including the free threaded build introduced in Python 3.13 and made officially supported in Python 3.14. Python 2 topics have been removed, because Python 2 reached end of life on 1 January 2020.

The guide has five parts: 27 questions for freshers, 19 for intermediate developers, 14 for experienced developers, 6 on NumPy and pandas for data roles, and 10 coding problems with solutions and time cost. Use the contents list below to jump to the part that matches your level.

Key points

  • 76 questions in five groups, from basic syntax to coding rounds.
  • Each answer opens with a short answer you can say in an interview.
  • All code was run on Python 3.13, with outputs shown.
  • Experienced answers include what the interviewer is listening for.
  • Python 2 content is removed because Python 2 ended on 1 January 2020.

In this guide

Python interview questions by section
Section Questions
Python Interview Questions for Freshers Q1 to Q27
Python Interview Questions for Intermediate Developers Q28 to Q46
Python Interview Questions for Experienced Developers Q47 to Q60
Python Interview Questions for Data Analyst and Data Science Roles Q61 to Q66
Python Coding Interview Questions with Solutions Q67 to Q76

Python Interview Questions for Freshers

These questions come up in almost every first round, whether the role is Python developer, data analyst or data scientist. Interviewers use them to check that your basics are solid before moving to harder topics, so short and correct answers matter more than long ones.

Q1. What is Python and why is it so widely used?

Python is a high level, general purpose programming language known for readable syntax and a large standard library. It is used for data analysis, machine learning, web development, automation and scripting. Teams choose it because code is quick to write and maintain, and libraries such as pandas, NumPy and scikit learn cover most data work.

Python is dynamically typed, supports object oriented, procedural and functional styles, and runs on Windows, macOS and Linux. The current stable releases are in the Python 3.13 and 3.14 line. All code on this page was run on Python 3.13.

Q2. Is Python a compiled or an interpreted language?

It is both in a technical sense. CPython compiles your source code to bytecode, stores it in .pyc files inside the __pycache__ folder, and then the Python virtual machine executes that bytecode. Since you never run a separate compile step yourself, Python is usually described as an interpreted language.

Other implementations behave differently. PyPy uses a just in time compiler to speed up long running programs, and Python 3.13 added an experimental JIT to CPython that is switched off by default.

Q3. What is PEP 8 and why does it matter?

PEP 8 is the official style guide for Python code. It recommends four spaces for indentation, snake_case names for functions and variables, PascalCase names for classes, and a line length of 79 characters. Following it makes code easier for other people to read, which is why teams enforce it with tools.

  • Linters and formatters used in practice: ruff, black and flake8.
  • Many teams relax the line length to 88 or 100 characters, so mention that the rule is a guideline.

Q4. What are the built in data types in Python?

The main built in types are int, float, complex and bool for numbers, str and bytes for text and binary data, list, tuple, set, frozenset and dict for collections, and NoneType for the value None. Python 3 has a single int type, so the old long type from Python 2 no longer exists.

python
values = [42, 3.14, 2 + 3j, True, "text", [1, 2], (1, 2), {1, 2}, {"a": 1}, None]

for v in values:
    print(type(v).__name__, end=" ")
output
int float complex bool str list tuple set dict NoneType

 

Q5. What is the difference between mutable and immutable objects?

A mutable object can be changed after it is created, and an immutable object cannot. Lists, dictionaries and sets are mutable. Integers, floats, strings, tuples and frozensets are immutable. Any operation that appears to change an immutable value actually creates a new object.

A common follow up is whether a tuple can hold a mutable item. It can. The tuple itself cannot be changed, but a list stored inside it can still be modified.

python
nums = [1, 2, 3]
nums[0] = 99
print(nums)

point = (1, 2, 3)
try:
    point[0] = 99
except TypeError as e:
    print(e)
output
[99, 2, 3]
'tuple' object does not support item assignment

 

Q6. What is the difference between a list, a tuple, a set and a dictionary?

A list is an ordered, changeable sequence. A tuple is an ordered, fixed sequence. A set holds unique values with no position. A dictionary stores key and value pairs with unique keys. Choose a list for a changing collection, a tuple for fixed records, a set for fast membership checks and a dictionary for lookups by key.

Python list, tuple, set and dict compared
Type Syntax Ordered Mutable Duplicates Typical use
list [1, 2, 3] Yes Yes Allowed A collection that grows or changes
tuple (1, 2, 3) Yes No Allowed Fixed records, return values, dictionary keys
set {1, 2, 3} No Yes Not allowed Removing duplicates, membership tests
dict {"a": 1} Insertion order kept Yes Keys must be unique Lookups by key, JSON like data

Dictionaries keep insertion order as a language guarantee from Python 3.7 onward. Sets do not guarantee any order.

Q7. How does Python manage memory?

Python keeps all objects in a private heap that the interpreter manages. Memory is freed mainly through reference counting, where an object is removed when nothing refers to it any more. A cyclic garbage collector runs in addition to clean up groups of objects that refer to each other.

python
import sys

a = []
print(sys.getrefcount(a))   # one reference from the name a, one from the call itself
b = a
print(sys.getrefcount(a))
output
2
3

 

Q8. What is the difference between == and is?

The == operator checks whether two objects have equal values. The is operator checks whether two names point to the very same object in memory. Use == to compare values and use is only for identity checks, most commonly with None, as in if value is None.

Do not rely on small integers or short strings being the same object. That is an implementation detail of CPython and can change between versions.

python
a = [1, 2]
b = [1, 2]
c = a
print(a == b, a is b, a is c)
output
True False True

Q9. What are *args and **kwargs?

The name *args collects extra positional arguments into a tuple, and **kwargs collects extra keyword arguments into a dictionary. They let a function accept a flexible number of inputs. The names args and kwargs are only convention. The single and double asterisk are what matter.

python
def show(*args, **kwargs):
    print(args)
    print(kwargs)

show(1, 2, city="Pune", course="Python")
output
(1, 2)
{'city': 'Pune', 'course': 'Python'}

 

Q10. What is a lambda function?

A lambda is a small anonymous function written in one line with the lambda keyword. It can take any number of arguments but holds only a single expression, whose value is returned. Lambdas are most useful as short throwaway functions, for example as the key argument of sorted, min or max.

python
students = [("Asha", 82), ("Ravi", 91), ("Meena", 75)]
print(sorted(students, key=lambda s: s[1], reverse=True))
output
[('Ravi', 91), ('Asha', 82), ('Meena', 75)]

 

Q11. How does slicing work in Python?

Slicing returns part of a sequence using the pattern sequence[start:stop:step]. The start index is included and the stop index is not. Negative indexes count from the end, and a step of -1 reverses the sequence. Slicing works on lists, tuples and strings, and it always returns a new object.

python
s = "interview"
print(s[0:5])
print(s[-4:])
print(s[::-1])

nums = [10, 20, 30, 40, 50]
print(nums[1:4], nums[::2])
output
inter
view
weivretni
[20, 30, 40] [10, 30, 50]

Q12. What is the difference between a shallow copy and a deep copy?

A shallow copy creates a new outer object but reuses the same inner objects, so changes to a nested item show up in both copies. A deep copy duplicates the outer object and everything nested inside it, so the two copies are fully independent. Use copy.copy for shallow and copy.deepcopy for deep.

python
import copy

original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original[0].append(99)
print(shallow)
print(deep)
output
[[1, 2, 99], [3, 4]]
[[1, 2], [3, 4]]

 

Q13. What is a list comprehension?

A list comprehension builds a new list from an iterable in one readable line, with an optional condition. It is usually shorter and faster than the same loop with append. Python also supports dictionary, set and generator comprehensions. Keep them simple, and switch to a normal loop when the logic gets long.

python
squares = [n * n for n in range(10) if n % 2 == 0]
print(squares)

lengths = {w: len(w) for w in ["python", "pandas", "numpy"]}
print(lengths)
output
[0, 4, 16, 36, 64]
{'python': 6, 'pandas': 6, 'numpy': 5}

 

Q14. How do you loop through a dictionary?

Loop over the dictionary directly to get its keys, use .values() to get the values, and use .items() to get key and value pairs together. The items method is the most common choice because it avoids a second lookup for each key.

python
marks = {"maths": 88, "python": 94, "sql": 79}

for subject, score in marks.items():
    print(subject, score)
print(list(marks.keys()))
print(list(marks.values()))
output
maths 88
python 94
sql 79
['maths', 'python', 'sql']
[88, 94, 79]

 

Q15. What is the difference between a module, a package and a library?

A module is a single Python file that contains code you can import. A package is a folder of modules, which Python recognises as a package for importing. A library is a general word for a collection of packages that offers related functionality, such as pandas or the standard library.

Importing works the same way for all three, using import or from and import. Third party libraries are installed with pip and live outside the standard library.

Q16. How do you handle exceptions in Python?

Put risky code in a try block and handle errors in one or more except blocks. The optional else block runs only if no error occurred, and the finally block always runs, which makes it the right place for cleanup. Catch specific exceptions rather than a bare except.

python
def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Cannot divide by zero")
    else:
        print("Result:", result)
    finally:
        print("Done")

divide(10, 2)
divide(1, 0)
output
Result: 5.0
Done
Cannot divide by zero
Done

 

Q17. What are f strings?

F strings are formatted string literals that start with the letter f and let you place Python expressions inside curly braces. They were added in Python 3.6 and are faster and easier to read than the older percent formatting or str.format. You can control number formats, padding and alignment inside the braces.

python
name = "Asha"
score = 91.456

print(f"{name} scored {score:.1f}")
print(f"{score=}")
print(f"{name:>10}|{score:08.2f}")
output
Asha scored 91.5
score=91.456
      Asha|00091.46

 

Q18. What is the difference between range and a list in Python 3?

In Python 3, range returns a lazy sequence that calculates values on demand and stores only its start, stop and step. A list stores every item in memory. The old xrange function from Python 2 no longer exists, because range now behaves the way xrange used to.

python
r = range(10 ** 9)
print(len(r), 999_999_999 in r)
print(list(range(2, 11, 3)))
output
1000000000 True
[2, 5, 8]

 

Q19. What does if __name__ == “__main__” do?

It runs a block of code only when the file is executed directly, and not when the file is imported as a module. When Python runs a file directly it sets the special variable __name__ to the string “__main__”. When another file imports it, __name__ holds the module name instead.

python
def main():
    print("Running as a script")

if __name__ == "__main__":
    main()
output
Running as a script

 

Q20. What is a virtual environment and how do you create one?

A virtual environment is an isolated folder with its own Python interpreter and installed packages. It stops one project from breaking another when they need different library versions. Create one with python -m venv, activate it, and then install the packages you need with pip.

Newer tools such as uv can create environments and install packages much faster, and are now common in teams. The idea of isolation stays the same.

bash
python -m venv .venv

# macOS or Linux
source .venv/bin/activate
# Windows
.venv\Scripts\activate

pip install pandas
pip freeze > requirements.txt

 

Q21. What is the difference between Python 2 and Python 3?

Python 3 is the current version, and Python 2 reached end of life on 1 January 2020. It no longer receives bug or security fixes, so new projects must use Python 3. The main differences are that print is a function, text is Unicode by default, and division of two integers returns a float.

Python 2 vs Python 3
Feature Python 2 Python 3
print Statement: print "hi" Function: print("hi")
Dividing 5 by 2 2 2.5
Default string type ASCII bytes Unicode text
range Returns a list (xrange is lazy) Lazy sequence
Support status Ended 1 January 2020 Actively maintained

 

Q22. What are the scope rules in Python (LEGB)?

Python looks up a name in four scopes in order: Local, Enclosing, Global and Built in, which is called the LEGB rule. A function sees its own names first, then names in any enclosing function, then module level names, and finally Python built ins. Use global or nonlocal to rebind names from an outer scope.

python
x = "global"

def outer():
    x = "enclosing"
    def inner():
        nonlocal x
        x = "changed by inner"
    inner()
    return x

print(outer())
print(x)
output
changed by inner
global

 

Q23. What is None in Python?

None is the single object of type NoneType and represents the absence of a value. It is not the same as 0, an empty string or False, although it counts as false in a condition. Functions that do not return anything return None, and you should test for it with the is operator.

python
print(None == 0, None is None, bool(None))

def no_return():
    pass

print(no_return())
output
False True False
None

 

Q24. What do break, continue and pass do?

The break statement exits the nearest loop completely. The continue statement skips the rest of the current iteration and moves to the next one. The pass statement does nothing and works as a placeholder where Python needs a statement, such as in an empty function or class.

python
for n in range(1, 8):
    if n == 3:
        continue
    if n == 6:
        break
    print(n, end=" ")
output
1 2 4 5

 

Q25. How do you read and write files safely?

Open files with the with statement. It closes the file automatically, even if an error occurs, so you do not leak file handles. Use mode “r” to read, “w” to write and replace, and “a” to append. For text files, pass encoding=”utf-8″ so the result does not depend on the operating system.

python
with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("line one\nline two\n")

with open("notes.txt", encoding="utf-8") as f:
    for line in f:
        print(line.strip())
output
line one
line two

 

Q26. What is the difference between append, extend and insert on a list?

The append method adds one item to the end of a list, extend adds every item of an iterable to the end, and insert puts an item at a chosen position. Append and extend are cheap. Insert near the start is slower because Python has to shift the items after it.

python
items = [1, 2]
items.append([3, 4])
print(items)

items = [1, 2]
items.extend([3, 4])
print(items)

items.insert(0, "first")
print(items)
output
[1, 2, [3, 4]]
[1, 2, 3, 4]
['first', 1, 2, 3, 4]

 

Q27. What is the difference between remove, pop and del for lists?

The remove method deletes the first item with a given value, pop removes and returns the item at a given index (the last item by default), and del removes an item or slice by index without returning it. Remove raises ValueError when the value is missing, and pop raises IndexError on an empty list.

python
nums = [10, 20, 30, 20, 40]
nums.remove(20)
print(nums)
print(nums.pop())
del nums[0]
print(nums)
output
[10, 30, 20, 40]
40
[30, 20]

 

Python Interview Questions for Intermediate Developers

Candidates with one to three years of experience are expected to write clean object oriented code and explain features such as decorators, generators and context managers. These questions test whether you have used Python on real projects and not only read about it.

Q28. What are the four pillars of object oriented programming in Python?

The four pillars are encapsulation, abstraction, inheritance and polymorphism. Encapsulation bundles data with the methods that use it, abstraction hides complexity behind a simple interface, inheritance lets a class reuse another class, and polymorphism lets different classes respond to the same method call in their own way.

Python has no strict private keyword. A single leading underscore marks a name as internal by convention, and a double leading underscore triggers name mangling.

python
class Animal:
    def __init__(self, name):
        self.name = name
    def speak(self):
        raise NotImplementedError

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow"

for pet in [Dog("Bruno"), Cat("Misty")]:
    print(pet.speak())
output
Bruno says Woof
Misty says Meow

 

Q29. What is the difference between class variables and instance variables?

A class variable is defined on the class and shared by every instance. An instance variable is set on self, usually inside __init__, and belongs to one object. Changing a class variable through the class affects all instances, while assigning through an instance creates a new instance variable that hides it.

python
class Course:
    platform = "Prwatech"          # class variable
    def __init__(self, name):
        self.name = name           # instance variable

a, b = Course("Python"), Course("SQL")
Course.platform = "Prwatech Online"
print(a.platform, b.platform)
a.platform = "Custom"
print(a.platform, b.platform)
output
Prwatech Online Prwatech Online
Custom Prwatech Online

 

Q30. What is the difference between __init__ and __new__, and what is self?

The __new__ method creates the object and the __init__ method then sets it up. Most classes only define __init__. The word self is the first parameter of an instance method and refers to the object the method is called on. Python passes it automatically, and the name is a convention.

python
class Demo:
    def __new__(cls, *args):
        print("__new__ creates the object")
        return super().__new__(cls)

    def __init__(self, value):
        print("__init__ sets it up")
        self.value = value

d = Demo(5)
print(d.value)
output
__new__ creates the object
__init__ sets it up
5

 

Q31. How does inheritance work, and what is the method resolution order?

A child class inherits attributes and methods from its parent class and can override them. When a class has several parents, Python searches them in a fixed order called the method resolution order, or MRO, which uses the C3 linearisation rule. The super function follows this order.

python
class A:
    def hello(self): return "A"

class B(A):
    def hello(self): return "B then " + super().hello()

class C(A):
    def hello(self): return "C then " + super().hello()

class D(B, C):
    pass

print([cls.__name__ for cls in D.__mro__])
print(D().hello())
output
['D', 'B', 'C', 'A', 'object']
B then C then A

 

Q32. What are decorators in Python?

A decorator is a function that takes another function and returns a modified version of it, which lets you add behaviour such as logging, timing, caching or access checks without editing the original code. You apply it with the @ symbol above the function. Use functools.wraps to keep the original name and docstring.

python
from functools import wraps

def log_call(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with {args}")
        result = func(*args, **kwargs)
        print(f"Returned {result}")
        return result
    return wrapper

@log_call
def add(a, b):
    return a + b

add(2, 3)
print(add.__name__)
output
Calling add with (2, 3)
Returned 5
add

 

Q33. What are generators and how is yield different from return?

A generator is a function that uses yield to produce values one at a time instead of building the whole result in memory. Each yield pauses the function and keeps its state, and the next call resumes from that point. The return statement ends the function completely and gives back one value.

Generators are the standard way to process large files or streams line by line without loading everything into memory.

python
def countdown(n):
    while n > 0:
        yield n
        n -= 1

gen = countdown(3)
print(next(gen), next(gen), next(gen))
print(sum(x * x for x in range(1_000_000)))
output
3 2 1
333332833333500000

 

Q34. What is the difference between an iterable and an iterator?

An iterable is any object you can loop over, such as a list, string or dictionary. It provides an __iter__ method that returns an iterator. An iterator is the object that produces the values one by one through __next__ and raises StopIteration when it runs out.

python
nums = [10, 20]
it = iter(nums)
print(next(it))
print(next(it))
try:
    next(it)
except StopIteration:
    print("exhausted")
output
10
20
exhausted

 

Q35. What is a context manager and how do you write one?

A context manager sets something up before a block runs and cleans it up afterwards, even when an error occurs. It is used with the with statement for files, locks and database connections. You can write one as a class with __enter__ and __exit__ methods, or with the contextlib.contextmanager decorator.

python
from contextlib import contextmanager

@contextmanager
def opened(name):
    print(f"open {name}")
    try:
        yield name
    finally:
        print(f"close {name}")

with opened("db") as conn:
    print("using", conn)
output
open db
using db
close db

 

Q36. What is the mutable default argument problem?

A default argument value is created once, when the function is defined, and not on every call. If the default is a mutable object such as a list, every call that does not pass its own value shares and changes the same object. The fix is to use None as the default and create the list inside the function.

python
def bad(item, bucket=[]):
    bucket.append(item)
    return bucket

def good(item, bucket=None):
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

print(bad(1), bad(2))
print(good(1), good(2))
output
[1, 2] [1, 2]
[1] [2]

 

Q37. What is the difference between @staticmethod, @classmethod and an instance method?

An instance method receives the object as self. A class method receives the class as cls and is often used as an alternative constructor. A static method receives neither and behaves like a plain function that lives inside the class for organisation.

python
class Student:
    def __init__(self, name, marks):
        self.name, self.marks = name, marks

    @classmethod
    def from_string(cls, text):
        name, marks = text.split(",")
        return cls(name, int(marks))

    @staticmethod
    def is_pass(marks):
        return marks >= 40

s = Student.from_string("Asha,82")
print(s.name, s.marks, Student.is_pass(s.marks))
output
Asha 82 True

 

Q38. What is the difference between __str__ and __repr__?

The __str__ method returns a readable description meant for end users and is used by print and str. The __repr__ method returns an unambiguous description meant for developers, ideally text that could recreate the object, and is used in the shell and in debugging. If only one is defined, define __repr__.

python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"
    def __str__(self):
        return f"({self.x}, {self.y})"

p = Point(2, 5)
print(p)
print(repr(p))
output
(2, 5)
Point(x=2, y=5)

 

Q39. What are dataclasses and when should you use them?

A dataclass is a class decorated with @dataclass that generates __init__, __repr__ and comparison methods from its annotated fields. Use it for classes that mainly store data, such as records, configuration and API responses. It removes boilerplate, and options such as frozen=True make instances immutable.

python
from dataclasses import dataclass, field

@dataclass(frozen=True)
class Lead:
    name: str
    city: str
    courses: tuple = ()

lead = Lead("Asha", "Pune", ("Python",))
print(lead)

@dataclass
class Batch:
    name: str
    students: list = field(default_factory=list)

b = Batch("Weekend")
b.students.append("Ravi")
print(b)
output
Lead(name='Asha', city='Pune', courses=('Python',))
Batch(name='Weekend', students=['Ravi'])

 

Q40. What are type hints and does Python enforce them?

Type hints are annotations that state the expected types of variables, parameters and return values. Python does not enforce them at runtime. Tools such as mypy and pyright read them to catch bugs before the code runs, and editors use them for autocomplete. Modern syntax includes list[int] and str | None.

python
def average(scores: list[float], default: float | None = None) -> float | None:
    if not scores:
        return default
    return sum(scores) / len(scores)

print(average([80, 90]))
print(average([], default=0.0))
print(average.__annotations__)
output
85.0
0.0
{'scores': list[float], 'default': float | None, 'return': float | None}

 

Q41. How do enumerate, zip, map and filter work?

The enumerate function pairs each item with its index, zip walks several iterables together and stops at the shortest, map applies a function to every item, and filter keeps the items for which a function returns true. All four return lazy iterators, so wrap them in list to see the result.

python
names = ["Asha", "Ravi", "Meena"]
marks = [82, 91, 75]

for i, name in enumerate(names, start=1):
    print(i, name)
print(list(zip(names, marks)))
print(list(map(lambda m: m + 5, marks)))
print(list(filter(lambda m: m > 80, marks)))
output
1 Asha
2 Ravi
3 Meena
[('Asha', 82), ('Ravi', 91), ('Meena', 75)]
[87, 96, 80]
[82, 91]

 

Q42. How do you create and raise custom exceptions?

Create a class that inherits from Exception, then raise it when your own rule is broken. Custom exceptions make error handling clearer, because callers can catch the exact case. Use raise new_error from original_error to keep the original cause visible in the traceback.

python
class InvalidMarksError(ValueError):
    pass

def grade(marks):
    if not 0 <= marks <= 100:
        raise InvalidMarksError(f"Marks must be 0 to 100, got {marks}")
    return "Pass" if marks >= 40 else "Fail"

try:
    grade(150)
except InvalidMarksError as e:
    print("Caught:", e)
output
Caught: Marks must be 0 to 100, got 150

 

Q43. What is the difference between pickle and JSON?

JSON is a text format that stores basic data such as strings, numbers, lists and dictionaries, and almost every language can read it. Pickle is a Python specific binary format that can store most Python objects. Never unpickle data from an untrusted source, because it can run arbitrary code.

python
import json, pickle

data = {"name": "Asha", "scores": [82, 91]}

text = json.dumps(data)
print(text)
print(json.loads(text) == data)
print(pickle.loads(pickle.dumps(data)) == data)
output
{"name": "Asha", "scores": [82, 91]}
True
True

 

Q44. What are Counter, defaultdict and deque in the collections module?

Counter counts how often items appear, defaultdict creates a default value for a missing key so you do not need to check first, and deque is a double ended queue with fast appends and pops at both ends. They solve common problems with less code than plain lists and dictionaries.

python
from collections import Counter, defaultdict, deque

print(Counter("mississippi").most_common(2))

groups = defaultdict(list)
for city, name in [("Pune", "Asha"), ("Bangalore", "Ravi"), ("Pune", "Meena")]:
    groups[city].append(name)
print(dict(groups))

q = deque([1, 2, 3])
q.appendleft(0)
q.pop()
print(q)
output
[('i', 4), ('s', 4)]
{'Pune': ['Asha', 'Meena'], 'Bangalore': ['Ravi']}
deque([0, 1, 2])

 

Q45. Why should you use the logging module instead of print?

Logging gives you severity levels, timestamps, formatting and control over where messages go, such as a file or a monitoring system. You can turn detailed debug messages on or off without editing code. Print has none of this and is meant for quick output, not for applications that run in production.

python
import logging, sys

logging.basicConfig(level=logging.INFO, stream=sys.stdout,
                    format="%(levelname)s | %(name)s | %(message)s")
log = logging.getLogger("leads")

log.debug("hidden at INFO level")
log.info("Loaded 120 rows")
log.warning("3 rows had missing phone numbers")
output
INFO | leads | Loaded 120 rows
WARNING | leads | 3 rows had missing phone numbers

 

Q46. How do you write unit tests in Python?

Write small functions that call your code and check the result with assert, then run them with pytest, which finds test files and reports failures clearly. The standard library also offers unittest. Good tests cover normal inputs, edge cases such as empty values, and the exceptions your code should raise.

Run the tests from the project folder with the command pytest.

python
# test_grade.py
import pytest
from grading import grade, InvalidMarksError

def test_pass():
    assert grade(75) == "Pass"

def test_fail():
    assert grade(20) == "Fail"

def test_invalid_marks():
    with pytest.raises(InvalidMarksError):
        grade(150)

 

Python Interview Questions for Experienced Developers

For candidates with four or more years of experience, interviewers move from syntax to how Python works underneath and how you design, test and ship code. Each answer below ends with a note on what the interviewer is listening for.

Q47. What is the GIL, and what changed with the free threaded build?

The Global Interpreter Lock lets only one thread run Python bytecode at a time in the standard CPython build, so CPU bound threads do not run in parallel. Python 3.13 added an optional free threaded build without the GIL, and Python 3.14 made it officially supported. It is a separate build, and the default build still has the GIL.

Threads still help with input and output bound work, such as network calls, because the GIL is released while waiting. Some third party extension modules may not yet support the free threaded build, so check library support before adopting it.

python
import sys

print(sys._is_gil_enabled())   # True on the standard build, False on a free threaded build
output
True

 

What the interviewer is listening for: You know the GIL limits CPU bound threads, you can name multiprocessing as the usual workaround, and you can say the free threaded build is optional and separate.

Q48. When should you use threading, multiprocessing or asyncio?

Use asyncio for many network or file waits handled in one thread, threading for blocking libraries that wait on input and output, and multiprocessing for CPU heavy work that needs several cores. The right choice depends on whether the program is waiting or computing.

asyncio vs threading vs multiprocessing
Tool Best for Parallel CPU work Main cost
asyncio Thousands of network calls No Code must use async libraries
threading Blocking input and output with older libraries No, in the standard build Shared state needs locks
multiprocessing CPU heavy tasks Yes Process start up and data copying
python
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

def square(n):
    return n * n

with ThreadPoolExecutor(max_workers=4) as pool:
    print(list(pool.map(square, [1, 2, 3, 4])))
output
[1, 4, 9, 16]

 

What the interviewer is listening for: You choose by workload type and mention the cost of each option instead of saying one is always faster.

Q49. How does async and await work in Python?

An async function returns a coroutine, and await pauses it until a result is ready, which lets the event loop run other tasks in the meantime. The code runs on one thread and switches only at await points. Use asyncio.gather to run several coroutines concurrently and asyncio.run to start the loop.

The three calls above finish in about 0.2 seconds in total, not 0.45, because the waits overlap. A blocking call such as time.sleep inside a coroutine freezes the whole loop.

python
import asyncio

async def fetch(name, delay):
    await asyncio.sleep(delay)
    return f"{name} done"

async def main():
    results = await asyncio.gather(fetch("A", 0.2), fetch("B", 0.1), fetch("C", 0.15))
    print(results)

asyncio.run(main())
output
['A done', 'B done', 'C done']

 

What the interviewer is listening for: You understand it is cooperative, single threaded concurrency and you can name the blocking call mistake.

Q50. What is a metaclass?

A metaclass is the class of a class. Just as an object is created from a class, a class is created from a metaclass, and the default one is type. Custom metaclasses let you change how classes are built, for example to register subclasses or enforce rules. Most problems are better solved with decorators or __init_subclass__.

python
registry = {}

class AutoRegister(type):
    def __new__(mcls, name, bases, attrs):
        cls = super().__new__(mcls, name, bases, attrs)
        registry[name] = cls
        return cls

class Plugin(metaclass=AutoRegister):
    pass

class CsvPlugin(Plugin):
    pass

print(sorted(registry))
print(type(Plugin))
output
['CsvPlugin', 'Plugin']
<class '__main__.AutoRegister'>

 

What the interviewer is listening for: You can explain it, and you also say that you would prefer simpler tools such as __init_subclass__ in most cases.

Q51. What are descriptors and how does the property decorator use them?

A descriptor is an object that defines __get__, and optionally __set__ or __delete__, and controls how an attribute is read or written on another class. The property decorator, class methods and functions themselves all work through this protocol. Use property when you want to validate or compute an attribute while keeping simple dot access.

python
class Account:
    def __init__(self, balance):
        self._balance = balance

    @property
    def balance(self):
        return self._balance

    @balance.setter
    def balance(self, value):
        if value < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = value

acc = Account(100)
acc.balance = 150
print(acc.balance)
try:
    acc.balance = -5
except ValueError as e:
    print(e)
output
150
Balance cannot be negative

 

What the interviewer is listening for: You connect property to the descriptor protocol and know it lets you add validation later without changing callers.

Q52. What is __slots__ and when is it useful?

Defining __slots__ in a class replaces the per instance dictionary with a fixed set of attributes. It lowers memory use and stops accidental new attributes, which matters when you create millions of small objects. The trade off is less flexibility, since you cannot add attributes that are not listed.

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

class Slotted:
    __slots__ = ("x", "y")
    def __init__(self, x, y):
        self.x, self.y = x, y

s = Slotted(1, 2)
try:
    s.z = 3
except AttributeError as e:
    print(e)
print(hasattr(Plain(1, 2), "__dict__"), hasattr(s, "__dict__"))
output
'Slotted' object has no attribute 'z' and no __dict__ for setting new attributes
True False

 

What the interviewer is listening for: You can give the memory reason and the flexibility cost, and you measure before using it.

Q53. How do weak references and the garbage collector handle reference cycles?

Reference counting alone cannot free objects that point to each other, so CPython runs a cyclic garbage collector that finds and removes unreachable groups. A weak reference points to an object without increasing its reference count, so it does not keep the object alive. Caches and observer lists use weak references to avoid leaks.

python
import weakref, gc

class Node:
    pass

n = Node()
ref = weakref.ref(n)
print(ref() is n)
del n
print(ref())

a, b = Node(), Node()
a.other, b.other = b, a      # a reference cycle
del a, b
print(gc.collect() > 0)
output
True
None
True

 

What the interviewer is listening for: You know reference counting is the main mechanism and that cycles need the collector.

Q54. How does a Python dictionary work internally, and what makes an object hashable?

A dictionary is a hash table. Python hashes the key, uses the result to find a slot, and compares keys on collision, which gives average constant time lookups. A key must be hashable, which means its hash never changes and it can be compared for equality. Lists and dictionaries are not hashable, but tuples of hashable items are.

python
d = {}
d[("Pune", 2026)] = "ok"
print(d)

try:
    d[["Pune", 2026]] = "fails"
except TypeError as e:
    print(e)
output
{('Pune', 2026): 'ok'}
unhashable type: 'list'

 

What the interviewer is listening for: You mention average constant time lookups, the hash and equality contract, and insertion order.

Q55. Does Python pass arguments by value or by reference?

Python passes object references by value, often called pass by assignment. The function receives a new name bound to the same object. Changing a mutable object inside the function is visible to the caller, but rebinding the name to a new object inside the function is not.

python
def change(items):
    items.append(99)       # mutates the caller's list
    items = [0]            # rebinds only the local name
    return items

data = [1, 2]
print(change(data))
print(data)
output
[0]
[1, 2, 99]

 

What the interviewer is listening for: You avoid the simple answers “by value” or “by reference” and explain with an example.

Q56. How do you find and fix performance problems in Python code?

Measure first. Use cProfile to find the slow functions, timeit to compare small alternatives, and line level or sampling profilers for detail. Then fix the biggest cost, usually with a better algorithm or data structure, vectorised NumPy and pandas operations, caching, or multiprocessing, before thinking about rewriting parts in another language.

The numbers from timeit depend on your machine, so run it yourself instead of copying figures from a page.

python
import cProfile, pstats, timeit

# Profile one call and show the 5 slowest functions
cProfile.run("main()", "profile.out")
pstats.Stats("profile.out").sort_stats("cumulative").print_stats(5)

# Compare two small alternatives
print(timeit.timeit("[i * 2 for i in range(1000)]", number=2000))
print(timeit.timeit("list(map(lambda i: i * 2, range(1000)))", number=2000))

 

What the interviewer is listening for: You say measure before optimising, and you reach for algorithms and vectorisation before low level tricks.

Q57. How do you manage packages and project dependencies?

Create a virtual environment for each project, list dependencies in pyproject.toml or requirements.txt, and pin versions so that builds repeat. Tools such as pip, Poetry and uv install and lock packages. A lock file records the exact versions, which keeps development, testing and production the same.

toml
# pyproject.toml

[project]
name = "lead-report"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "pandas>=2.2",
    "requests>=2.32",
]

[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]

 

What the interviewer is listening for: You use isolated environments, pinned versions and a repeatable install, and you can name at least one modern tool.

Q58. How do you call large language model APIs reliably from Python?

Treat the API like any remote service. Set timeouts, retry only temporary errors with exponential backoff, keep keys in environment variables and never in code, validate the response structure before using it, and log token use and errors. Use streaming when users wait for long answers.

The function above simulates a flaky service, so you can run it without an account. In real code the call would use the provider client library, and you would retry only on rate limit and server errors.

python
import time, random
from functools import wraps

def retry(times=3, base_delay=0.01):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except ConnectionError as e:
                    print(f"attempt {attempt} failed: {e}")
                    if attempt == times:
                        raise
                    time.sleep(base_delay * 2 ** attempt)
        return wrapper
    return decorator

calls = {"n": 0}

@retry(times=3)
def call_model(prompt):
    calls["n"] += 1
    if calls["n"] < 3:
        raise ConnectionError("temporary network error")
    return {"text": "ok", "prompt": prompt}

print(call_model("summarise this"))
output
attempt 1 failed: temporary network error
attempt 2 failed: temporary network error
{'text': 'ok', 'prompt': 'ollama'}

 

What the interviewer is listening for: You think about failure, cost and secrets, not only about the prompt.

Q59. How do you implement common design patterns in Python?

Python often needs less ceremony than other languages. A module is already a singleton, a dictionary of functions can replace a factory class, and passing functions as arguments covers the strategy pattern. Use classes when you need shared state, and use the simplest structure that keeps the code testable.

python
def csv_report(rows):  return "csv:" + ",".join(rows)
def json_report(rows): return "json:" + str(rows)

FACTORY = {"csv": csv_report, "json": json_report}

def build(kind, rows):
    try:
        return FACTORY[kind](rows)
    except KeyError:
        raise ValueError(f"Unknown report type: {kind}")

print(build("csv", ["a", "b"]))
print(build("json", ["a", "b"]))
output
csv:a,b
json:['a', 'b']

 

What the interviewer is listening for: You prefer simple Python idioms over copying class heavy patterns from Java.

Q60. How do you structure and test a large Python project?

Use a src layout with one package, keep configuration separate from code, write small functions that are easy to test, and run linting, type checking and tests in a continuous integration pipeline on every change. Add pre commit hooks so style problems are caught before review.

  • Keep the code in a src folder with one package inside it, tests in a tests folder, and settings in environment variables or a config file.
  • Run ruff for linting and formatting, mypy or pyright for types, and pytest with coverage for tests.
  • Pin dependencies with a lock file and build the project in a clean environment in the pipeline.
  • Document how to run the project in a README, including setup and test commands.

What the interviewer is listening for: You describe a repeatable workflow with tests and automation, not just folder names.

Python Interview Questions for Data Analyst and Data Science Roles

If you are applying for a data analyst, data scientist or machine learning role, expect questions on NumPy and pandas after the core language round. These are the ones that appear most often.

Q61. What is the difference between a NumPy array and a Python list?

A NumPy array stores items of one data type in a compact block of memory and supports fast vectorised maths on the whole array at once. A Python list can hold mixed types and works item by item, which is slower for numeric work. Arrays also support broadcasting and multi dimensional indexing.

Notice that multiplying a list by 2 repeats it, while multiplying an array by 2 doubles each value. Interviewers like to see that you know this.

python
import numpy as np

lst = [1, 2, 3]
arr = np.array([1, 2, 3])

print(lst * 2)
print(arr * 2)
print(arr.dtype, arr.shape)

matrix = np.arange(6).reshape(2, 3)
print(matrix)
print(matrix.mean(axis=0))
output
[1, 2, 3, 1, 2, 3]
[2 4 6]
int64 (3,)
[[0 1 2]
 [3 4 5]]
[1.5 2.5 3.5]

 

Q62. What is the difference between a pandas Series and a DataFrame?

A Series is a one dimensional labelled array, like a single column. A DataFrame is a two dimensional table of rows and columns where each column is a Series. Both have an index that labels the rows, and most analysis in pandas happens on DataFrames.

python
import pandas as pd

df = pd.DataFrame({
    "city": ["Pune", "Bangalore", "Pune", "Bangalore"],
    "course": ["Python", "Python", "SQL", "Data Science"],
    "fee": [18000, 18000, 12000, 30000],
})

print(type(df["fee"]).__name__, type(df).__name__)
print(df.head(2))
print(df.shape)
output
Series DataFrame
        city  course    fee
0       Pune  Python  18000
1  Bangalore  Python  18000
(4, 3)

 

Q63. How do you handle missing values in pandas?

First find them with isna and sum, then decide based on the column. You can drop rows with dropna, fill with a fixed value, the mean, median or the previous value using fillna, or leave them if the model can handle them. The right choice depends on how much is missing and why.

In an interview, say that you check why the values are missing before filling them. Filling with the mean can distort the distribution when many values are missing.

python
import pandas as pd
import numpy as np

df = pd.DataFrame({"age": [25, np.nan, 31, 28], "city": ["Pune", "Pune", None, "Bangalore"]})

print(df.isna().sum())
df["age"] = df["age"].fillna(df["age"].median())
df["city"] = df["city"].fillna("Unknown")
print(df)
output
age     1
city    1
dtype: int64
    age       city
0  25.0       Pune
1  28.0       Pune
2  31.0    Unknown
3  28.0  Bangalore

 

Q64. What is the difference between merge, join and concat in pandas?

The merge function combines two DataFrames on matching column values, like a SQL join. The join method does the same using the index by default. The concat function stacks DataFrames on top of each other or side by side without matching on values. Pick the how argument, such as inner or left, to control which rows are kept.

python
import pandas as pd

students = pd.DataFrame({"id": [1, 2, 3], "name": ["Asha", "Ravi", "Meena"]})
scores = pd.DataFrame({"id": [1, 2, 4], "score": [82, 91, 70]})

print(students.merge(scores, on="id", how="left"))
print(students.merge(scores, on="id", how="inner"))
print(pd.concat([students, students], ignore_index=True).shape)
output
   id   name  score
0   1   Asha   82.0
1   2   Ravi   91.0
2   3  Meena    NaN
   id  name  score
0   1  Asha     82
1   2  Ravi     91
(6, 2)

 

Q65. How does groupby work in pandas?

The groupby method splits the data into groups based on one or more columns, applies a function such as sum, mean or count to each group, and combines the results. It follows the split, apply, combine pattern, and agg lets you run several calculations at once.

python
import pandas as pd

df = pd.DataFrame({
    "city": ["Pune", "Bangalore", "Pune", "Bangalore", "Pune"],
    "fee": [18000, 18000, 12000, 30000, 30000],
})

print(df.groupby("city")["fee"].agg(["count", "sum", "mean"]))
output
           count    sum     mean
city
Bangalore      2  48000  24000.0
Pune           3  60000  20000.0

 

Q66. Why is vectorisation faster than using apply or loops in pandas?

Vectorised operations run in optimised compiled code over a whole column at once, while apply and loops call a Python function for every row. For arithmetic, comparisons and string operations that pandas already supports, the vectorised form is shorter and usually much faster. Use apply only when no built in method exists.

python
import pandas as pd

df = pd.DataFrame({"fee": [18000, 12000, 30000]})

slow = df["fee"].apply(lambda x: x * 1.18)   # runs Python code per row
fast = df["fee"] * 1.18                      # vectorised

print(fast.tolist())
print(slow.equals(fast))
output
[21240.0, 14160.0, 35400.0]
True

 

Python Coding Interview Questions with Solutions

Most technical rounds include a live coding problem. For each one below, state your approach, write the code, and then explain the time and space cost. Interviewers care about your reasoning as much as the final answer.

Q67. How do you check whether a string is a palindrome, ignoring case and punctuation?

Keep only letters and digits, convert them to lower case, and compare the result with its reverse. This takes linear time and linear extra space. A two pointer version that compares from both ends uses constant extra space if the string has already been cleaned.

python
def is_palindrome(text):
    cleaned = [ch.lower() for ch in text if ch.isalnum()]
    return cleaned == cleaned[::-1]

print(is_palindrome("A man, a plan, a canal: Panama"))
print(is_palindrome("Prwatech"))
output
True
False

 

Q68. Two sum: find the indexes of two numbers that add up to a target.

Store each number and its index in a dictionary as you scan the list. For every number, check whether target minus the number is already stored. This finds the pair in one pass with linear time and linear space, compared with quadratic time for checking every pair.

python
def two_sum(nums, target):
    seen = {}
    for i, n in enumerate(nums):
        if target - n in seen:
            return [seen[target - n], i]
        seen[n] = i
    return []

print(two_sum([2, 7, 11, 15], 9))
print(two_sum([3, 2, 4], 6))
output
[0, 1]
[1, 2]

 

Q69. How do you check whether two strings are anagrams?

Two strings are anagrams if they contain the same characters with the same counts. Count the characters of each string with Counter and compare the results. This runs in linear time. Sorting both strings also works but takes n log n time.

python
from collections import Counter

def is_anagram(a, b):
    return Counter(a.lower()) == Counter(b.lower())

print(is_anagram("Listen", "Silent"))
print(is_anagram("Python", "Typhon"))
output
True
True

 

Q70. How do you find the first non repeating character in a string?

Count every character in one pass, then scan the string again and return the first character whose count is one. Both passes are linear, so the total time is linear, and the extra space is limited by the size of the alphabet.

python
from collections import Counter

def first_unique(text):
    counts = Counter(text)
    for ch in text:
        if counts[ch] == 1:
            return ch
    return None

print(first_unique("swiss"))
print(first_unique("aabb"))
output
w
None

 

Q71. How do you flatten a nested list of any depth?

Walk through the list, and whenever an item is itself a list, flatten it recursively and add its items to the result. Using a generator keeps memory use low. For very deep nesting, an explicit stack avoids hitting the recursion limit.

python
def flatten(items):
    for item in items:
        if isinstance(item, list):
            yield from flatten(item)
        else:
            yield item

print(list(flatten([1, [2, [3, 4]], 5, [[6]]])))
output
[1, 2, 3, 4, 5, 6]

 

Q72. How do you count word frequency and return the top three words?

Lower case the text, split it into words, count them with Counter, and call most_common with the number you need. The count takes linear time, and most_common uses a heap, which is efficient when you only need the top few.

python
from collections import Counter
import re

text = "Python is simple. Python is popular. Learn Python and practise Python daily."
words = re.findall(r"[a-z']+", text.lower())
print(Counter(words).most_common(3))
output
[('python', 4), ('is', 2), ('simple', 1)]

 

Q73. How do you write a Fibonacci function that does not repeat work?

A plain recursive version recalculates the same values many times and takes exponential time. Add memoisation with functools.lru_cache so each value is computed once, which brings the time down to linear. An iterative loop does the same with constant extra space.

python
from functools import lru_cache

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

print([fib(i) for i in range(10)])
print(fib(80))
output
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
23416728348467685

 

Q74. How do you merge overlapping intervals?

Sort the intervals by start value, then walk through them. If the current interval starts before the previous one ends, extend the previous end. Otherwise start a new interval. Sorting dominates the cost, so the total time is n log n.

python
def merge(intervals):
    merged = []
    for start, end in sorted(intervals):
        if merged and start <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], end)
        else:
            merged.append([start, end])
    return merged

print(merge([[1, 3], [8, 10], [2, 6], [15, 18], [17, 20]]))
output
[[1, 6], [8, 10], [15, 20]]

 

Q75. How do you check whether brackets in a string are balanced?

Use a stack. Push each opening bracket, and on a closing bracket pop the stack and check that the two match. The string is balanced if every closing bracket matched and the stack is empty at the end. This takes linear time.

python
def is_balanced(text):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for ch in text:
        if ch in "([{":
            stack.append(ch)
        elif ch in pairs:
            if not stack or stack.pop() != pairs[ch]:
                return False
    return not stack

print(is_balanced("{[()]}"))
print(is_balanced("{[(])}"))
output
True
False

 

Q76. Using pandas, how do you find the top two courses by revenue in each city?

Group the data by city and course, add up the revenue, sort within each city, and keep the first two rows of each group with head. This pattern, group then rank then filter, is the pandas version of a SQL window function and is a very common analyst test.

python
import pandas as pd

sales = pd.DataFrame({
    "city":    ["Pune", "Pune", "Pune", "Bangalore", "Bangalore", "Bangalore"],
    "course":  ["Python", "SQL", "Data Science", "Python", "SQL", "Data Science"],
    "revenue": [180000, 96000, 300000, 220000, 84000, 360000],
})

top2 = (sales.sort_values(["city", "revenue"], ascending=[True, False])
             .groupby("city")
             .head(2))
print(top2.to_string(index=False))
output
     city       course  revenue
Bangalore Data Science   360000
Bangalore       Python   220000
     Pune Data Science   300000
     Pune       Python   180000

 

Common questions about Python interviews

What do Python interviews usually cover?

Most Python interviews cover core language topics such as data types, functions, object oriented programming and exception handling, followed by a coding problem. For data roles they add NumPy, pandas and SQL, and for experienced roles they add concurrency, memory management, testing and project design.

How should a fresher prepare for a Python interview in 30 days?

Spend the first week on basics, the second on object oriented programming and decorators, the third on NumPy, pandas and coding problems, and the last week on mock interviews. Write every answer in code yourself and run it. Reading answers without running them is the most common reason freshers stall.

Do Python interviews include data structures and algorithms?

Yes, most technical rounds include at least one problem on lists, dictionaries, strings or sorting, and product companies add harder problems. For analyst roles the problems are usually lighter and mix Python with a SQL or pandas task. Practise the coding questions in this guide and explain the time cost of each.

Which Python version should I mention in an interview?

Mention Python 3, and say you work with a current release such as Python 3.13 or 3.14. Avoid describing Python 2 features as current, because Python 2 reached end of life on 1 January 2020. Knowing what changed in recent releases, such as the free threaded build, signals that your knowledge is up to date.

Is Python alone enough to get a data analyst job?

Python alone is usually not enough. Data analyst interviews commonly test SQL, spreadsheet skills and a reporting tool such as Power BI or Tableau alongside pandas. Use Python to show you can clean data, answer a business question and explain the result clearly, since employers look for that combination.

A four week plan to prepare

Use this plan if you have about a month before your interview. Adjust the pace if you already know the basics.

Four week Python interview study plan
Week Focus What to do
Week 1 Basics: data types, strings, loops, functions, files, exceptions Freshers section, Q1 to Q27. Solve three small problems each day.
Week 2 Object oriented code, decorators, generators, context managers Intermediate section. Rewrite each example from memory without looking.
Week 3 NumPy, pandas and coding problems Python for data section and the coding section. Time yourself on each problem.
Week 4 Experienced topics and mock interviews Experienced section. Explain each answer aloud in under one minute, then take a mock interview.

Practise with a trainer

If you want to practise these questions with a trainer and build a portfolio project along the way, you can book a free demo class for the Prwatech Python course. For data roles, the Data Science course covers Python, statistics, machine learning and projects.

Contact the learning team for current batch dates and fees.

Written by Mithun Ray, data engineering trainer at Prwatech. Reviewed by Avishekh Verma. Last updated October,2026.

Popular Tags:

Python Interview Questions and answers