25 Python Interview Questions for Freshers (with Answers and Code)
These are the Python questions freshers are commonly asked in campus placements and first technical rounds: mutable default arguments, is vs ==, list vs tuple, shallow vs deep copy, the GIL, and short coding problems like two-sum and palindrome checks. Each one below has a short, correct answer you can say out loud, and every coding answer is runnable code with its output.
Read each answer, then run the code and change it; interviewers always follow up with "what if...".
Basics
1. Is Python compiled or interpreted?
Both. CPython, the standard implementation, compiles source to bytecode (cached in .pyc files), then its interpreter executes that bytecode. You never run a separate compile step, so Python is usually called interpreted.
2. What is the difference between mutable and immutable types?
A mutable object can be changed in place: list, dict, set. An immutable object cannot: int, float, str, tuple, frozenset. "Changing" a string actually creates a new string object.
3. What is the difference between is and ==?
== compares values. is compares identity, meaning whether both names point to the same object in memory.
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True same contents
print(a is b) # False two different list objects
print(a is c) # True same object
x = None
print(x is None) # True
Use is only for singletons like None. Never use it to compare numbers or strings; caching makes it work by accident sometimes.
4. What is the difference between a list and a tuple?
A list is mutable and uses square brackets; a tuple is immutable and uses round brackets. Because a tuple of immutable items is hashable, it can be a dictionary key or a set member, and a list cannot.
point = (3, 4)
distances = {point: 5.0}
print(distances[(3, 4)]) # 5.0
Use a tuple for a fixed record (coordinates, a row from a database), and a list for a collection that grows or changes.
5. What is the difference between /, // and %?
print(7 / 2) # 3.5 true division, always a float
print(7 // 2) # 3 floor division
print(-7 // 2) # -4 rounds down, not towards zero
print(-7 % 2) # 1 result takes the sign of the divisor
The negative case is the one interviewers ask about.
Data structures
6. Why is checking x in my_set faster than x in my_list?
A list is scanned element by element: O(n). A set or dict is a hash table, so Python hashes x and jumps straight to its slot: O(1) on average. The hashing and O(1) lookup lesson shows why, and the Big-O lesson covers how to state complexity in an interview.
7. What can be used as a dictionary key?
Any hashable object: strings, numbers, tuples of immutable items. Lists, dicts and sets raise TypeError: unhashable type.
8. What is the difference between a shallow copy and a deep copy?
A shallow copy makes a new outer container that shares the inner objects. A deep copy copies everything recursively.
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original[0].append(99)
print(shallow) # [[1, 2, 99], [3, 4]]
print(deep) # [[1, 2], [3, 4]]
list.copy(), list(x) and slicing with x[:] are all shallow copies.
9. What is a list comprehension?
A compact way to build a list from an iterable, optionally with a filter:
squares_of_evens = [n * n for n in range(10) if n % 2 == 0]
print(squares_of_evens) # [0, 4, 16, 36, 64]
OOP
10. What is self?
self is the instance a method was called on. obj.method() is effectively ClassName.method(obj), so Python passes the object as the first argument. The name self is a convention, not a keyword. The classes and objects lesson covers this with exercises.
11. What is the difference between a class attribute and an instance attribute?
A class attribute is shared by every instance; an instance attribute belongs to one object. The classic trap is a mutable class attribute:
class Team:
members = [] # class attribute: shared
def __init__(self, name):
self.name = name # instance attribute: per object
a = Team("A")
b = Team("B")
a.members.append("Ravi")
print(b.members) # ['Ravi'] b sees a's change
Fix it by creating self.members = [] inside __init__.
12. What is the difference between __str__ and __repr__?
__str__ is the readable form used by print(). __repr__ is the unambiguous, developer-facing form used in the REPL and inside containers.
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, 3)
print(p) # (2, 3)
print(repr(p)) # Point(x=2, y=3)
print([p]) # [Point(x=2, y=3)]
13. What does super() do?
It calls the next class in the method resolution order (MRO), usually the parent class:
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "Woof, parent says " + super().speak()
print(Dog().speak()) # Woof, parent says ...
print([c.__name__ for c in Dog.__mro__]) # ['Dog', 'Animal', 'object']
Functions, decorators and generators
14. What is the mutable default argument problem?
Default values are evaluated once, when the function is defined, not on every call. A mutable default is therefore shared across calls:
def add_item(item, basket=[]):
basket.append(item)
return basket
print(add_item("pen")) # ['pen']
print(add_item("book")) # ['pen', 'book'] the same list again
def add_item_fixed(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
print(add_item_fixed("pen")) # ['pen']
print(add_item_fixed("book")) # ['book']
15. What are *args and **kwargs?
*args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict.
def show(*args, **kwargs):
print(args, kwargs)
show(1, 2, city="Pune") # (1, 2) {'city': 'Pune'}
16. What is a decorator?
A function that takes a function and returns a new function that wraps it, adding behaviour before or after the call. @name above a def is shorthand for func = name(func).
from functools import wraps
def log_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"calling {func.__name__}{args}")
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
print(add(2, 3))
# calling add(2, 3)
# 5
print(add.__name__) # add
Without functools.wraps, add.__name__ would be "wrapper". Practise writing one in the decorators lesson.
17. What is a generator, and how is it different from a list?
A function containing yield returns a generator, which produces values one at a time, pausing between them. It uses almost no memory, but can be iterated only once and has no len() or indexing. See the generators lesson.
18. What is a lambda?
A small anonymous function limited to a single expression. It is most useful as a key argument:
students = [("Asha", 82), ("Vikram", 91), ("Neha", 76)]
print(sorted(students, key=lambda s: s[1], reverse=True))
# [('Vikram', 91), ('Asha', 82), ('Neha', 76)]
19. What is the GIL?
The Global Interpreter Lock in CPython allows only one thread to execute Python bytecode at a time. So threads do not speed up CPU-heavy pure-Python work, but they still help with I/O-bound work (network calls, file reads), because the lock is released while waiting. For CPU-bound work, use multiprocessing. Python 3.12 still has the GIL. Python 3.13 added an optional free-threaded build without it, but it is a separate build, not the default.
Coding problems
20. Reverse a string
s = "Python"
print(s[::-1]) # nohtyP
print("".join(reversed(s))) # nohtyP
21. Check whether a string is a palindrome
def is_palindrome(s):
cleaned = [ch.lower() for ch in s if ch.isalnum()]
return cleaned == cleaned[::-1]
print(is_palindrome("Madam")) # True
print(is_palindrome("A man, a plan, a canal: Panama")) # True
print(is_palindrome("placement")) # False
If asked to do it without building a reversed copy, use two pointers moving inward, which needs O(1) extra space. The two pointers lesson teaches this pattern.
def is_palindrome_in_place(s):
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
print(is_palindrome_in_place("racecar")) # True
print(is_palindrome_in_place("python")) # False
22. Two sum: find indices of two numbers that add up to a target
The brute-force answer checks every pair in O(n²). The expected answer uses a dict for O(n):
def two_sum(nums, target):
seen = {} # value -> index
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)) # [0, 1]
print(two_sum([3, 2, 4], 6)) # [1, 2]
23. Find the duplicates in a list
from collections import Counter
def find_duplicates(items):
seen, dupes = set(), set()
for x in items:
if x in seen:
dupes.add(x)
else:
seen.add(x)
return sorted(dupes)
data = [4, 1, 4, 2, 7, 2, 4]
print(find_duplicates(data)) # [2, 4]
print([x for x, count in Counter(data).items() if count > 1]) # [4, 2]
Both are O(n).
24. FizzBuzz
def fizzbuzz(n):
if n % 15 == 0:
return "FizzBuzz"
if n % 3 == 0:
return "Fizz"
if n % 5 == 0:
return "Buzz"
return str(n)
print(" ".join(fizzbuzz(n) for n in range(1, 16)))
# 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
Check 15 first; if you test 3 first, 15 prints "Fizz".
25. Count how often each character appears
def char_count(s):
counts = {}
for ch in s:
counts[ch] = counts.get(ch, 0) + 1
return counts
print(char_count("banana")) # {'b': 1, 'a': 3, 'n': 2}
How to prepare
Follow-ups are always variations: "what if the list is sorted?", "can you do it without extra space?", "what is the complexity?". Run each snippet in the browser Python terminal, break it, and fix it. Then solve fresh problems on the practice page; Python runs in the browser with no install.
Projects cover the rest of the interview; see 10 Python projects for placement interviews.