Python Basics • Coding Tasks • OOP Basics • College Projects • 2026

Python Interview Questions for Freshers

Python fresher rounds test whether you can explain the basics in your own words, write small programs cleanly and talk honestly about what you built. Expect questions on slicing, list and dict methods, scope, truthiness, files and exceptions, a few short coding tasks, and your college or internship projects. It is written for final-year students, new graduates and anyone moving into a first Python job from an internship or a course. Read each sample answer, then say your own version out loud. For deeper topics like generators, decorators and the GIL, go to the main Python page.

Search all questions by round, difficulty and level, or save the ones you want to practice.

Basics 6 questions

Easy Technical round Fresher Practice question

1. Python is often called interpreted and dynamically typed. What do those two words mean when you actually write code?

What the interviewer is really testing:
Whether you can explain the basics of the language in your own words instead of reciting a textbook line.
Answer frame:

Interpreted: the standard interpreter compiles your file to bytecode and runs it straight away, with no separate build step.

Dynamic typing: types belong to objects, not to variable names, and are checked while the program runs.

Strong typing: Python still refuses to mix types silently, so "3" + 4 raises a TypeError.

Sample spoken answer:

“Interpreted means I don't run a separate compile step. When I run a file, the standard interpreter turns it into bytecode and executes that on its own virtual machine right away. The catch is that some mistakes, like a typo in a branch that rarely runs, only show up when that line actually executes. Dynamically typed means a variable is just a name pointing at an object, and the type lives on the object. So x can point to an int and later to a string, and nothing complains until I use it wrongly. But Python is also strongly typed: adding the string three to the number four raises a TypeError instead of guessing. That's why I lean on tests and type hints to catch mistakes early.”

Red flag to avoid:

Saying Python has no compile step at all, or that dynamic typing means Python converts types for you.

They may ask next:
  • If types are only checked at runtime, how do you catch type mistakes before running the code?
  • What is a .pyc file, and where does it come from?
Say it in 60 seconds
Medium Technical round Fresher Practice question

2. What do break, continue and pass do? And what does an else block after a for loop mean?

What the interviewer is really testing:
Whether you know the loop controls, including the loop else that many freshers have never used.
Answer frame:

break: leaves the loop at once.

continue: skips the rest of this pass and moves to the next item.

pass: does nothing; it holds a place where Python needs a statement.

Loop else: runs only when the loop finished without hitting break.

Sample spoken answer:

“break stops the loop immediately and jumps past it. continue skips the rest of the current pass and goes to the next item. pass does nothing at all; I use it when Python needs a statement, like an empty function or class I'll fill in later. The loop else is the one people forget. It runs only if the loop ended normally, without a break. That makes it handy for searching: I loop over items, break when I find a match, and put the not-found handling in the else. It saves me from keeping a separate found flag. The same else works on a while loop too.”

Code:
def find_user(users, name):
    for user in users:
        if user == name:
            print("found", user)
            break
    else:
        print("not found")
Red flag to avoid:

Thinking pass works like continue, or that a loop else runs whenever an if inside the loop was false.

They may ask next:
  • Does the else block run if the list is empty?
  • How would you write the same search without the loop else?
Say it in 60 seconds
Easy Technical round Fresher Practice question

3. What does if __name__ == "__main__": do at the bottom of a script, and why do people write it?

What the interviewer is really testing:
Whether you know how a module behaves when it is run directly versus imported.
Answer frame:

__name__: every module has one; it is "__main__" when the file is run directly.

On import: __name__ is the module's own name, so the guarded block is skipped.

Why: the file can be both a runnable script and an importable module without side effects.

Sample spoken answer:

“Every Python module gets a variable called __name__. When I run a file directly, Python sets it to the string __main__. When another file imports it, __name__ is the module's own name instead. So the code inside that if-block only runs when I launch the file as a script, not when someone imports it. That lets me keep reusable functions at the top and a small entry point at the bottom, like parsing arguments and calling main(). Without the guard, importing my file in a test would run the whole script. It also matters for multiprocessing on systems that start new processes by re-importing the main file, where a missing guard can make the program start processes over and over.”

Code:
def main():
    print("running as a script")

if __name__ == "__main__":
    main()
Red flag to avoid:

Saying it is required for a script to run at all, or that it makes the code run faster.

They may ask next:
  • What would happen if you imported a script without this guard that deletes files at the top level?
  • Why do many people put the logic in a main() function instead of directly under the if?
Say it in 60 seconds
Easy Technical round Fresher Practice question

4. Which values count as false in an if statement, and when is if not x: the wrong check to write?

What the interviewer is really testing:
Whether you know Python's truthiness rules and can spot the bug where a valid zero is treated as missing.
Answer frame:

Falsy values: None, False, zero of any number type, and empty strings, lists, tuples, dicts and sets.

Everything else: is truthy, including the string "0" and a list holding one empty item.

The trap: when 0 or an empty string is a valid value, check x is None instead.

Sample spoken answer:

“The falsy values are None, False, zero in any number type, and empty containers: an empty string, list, tuple, dict or set. Everything else is truthy, including the string with a zero in it, which catches people out. My own classes can decide too, through __bool__ or __len__. if not x: is fine when I just mean 'is this empty'. It's wrong when zero or an empty string is a real answer. Say a function returns a discount that could be zero, or None when there's no discount. If I write if not discount, a real zero discount gets treated as missing. There I write if discount is None so only the missing case is caught.”

Red flag to avoid:

Saying the string "0" or "False" is falsy, or using if not x for a value where zero is valid.

They may ask next:
  • What does bool([[]]) return, and why?
  • How does Python decide whether an object of your own class is true or false?
Say it in 60 seconds
Medium Technical round Fresher Practice question

5. What do true division, floor division and the modulo operator return in Python 3, and why does 0.1 + 0.2 == 0.3 come out False?

What the interviewer is really testing:
Whether you know the arithmetic rules that trip up freshers, including floor division with negatives and float precision.
Answer frame:

Slash: always gives a float, even for 6 / 3.

Floor division: rounds down toward negative infinity, so -7 // 2 is -4.

Modulo: the remainder takes the sign of the divisor, so minus seven modulo two is 1.

Floats: stored in binary, so 0.1 is only approximate; compare with math.isclose or use Decimal.

Sample spoken answer:

“In Python 3, a single slash is true division and always gives a float, so six divided by three is 2.0. Double slash is floor division: it rounds down, toward negative infinity, not toward zero. So seven floor-divided by two is 3, but minus seven floor-divided by two is minus 4. The modulo operator gives the remainder, and its sign follows the divisor, so minus seven modulo two is 1. That keeps floor division and the remainder consistent with each other. On floats, 0.1 can't be stored exactly in binary, so 0.1 plus 0.2 ends up a tiny bit off from 0.3 and the equality check fails. For comparisons I use math.isclose, and for money-like values where every digit matters I'd use the Decimal type.”

Code:
import math
6 / 3       # 2.0
7 // 2      # 3
-7 // 2     # -4
-7 % 2      # 1
0.1 + 0.2 == 0.3               # False
math.isclose(0.1 + 0.2, 0.3)   # True
Red flag to avoid:

Saying // rounds toward zero, or suggesting floats are fine for exact values like account balances.

They may ask next:
  • What does round(2.5) return in Python 3?
  • How would you check whether a number is even without the modulo operator?
Say it in 60 seconds
Easy Technical round Fresher Practice question

6. What is a virtual environment, and how do you create one and install a project's packages into it?

What the interviewer is really testing:
Whether you have set up a Python project yourself, which most teams expect on day one.
Answer frame:

What: a folder with its own interpreter link and its own installed packages, separate per project.

Create: python -m venv .venv, then activate it.

Install: pip install -r requirements.txt; record versions so others get the same setup.

Sample spoken answer:

“A virtual environment is a folder that gives one project its own set of installed packages. Without it, every project on my machine shares one global set, so two projects that need different versions of the same library end up fighting. I create one with python -m venv .venv in the project folder, then activate it: source .venv/bin/activate on macOS or Linux, or the activate script in the Scripts folder on Windows. After that, pip install puts packages inside that folder only. To share the setup, I list the packages in requirements.txt and teammates run pip install -r requirements.txt. I don't commit the .venv folder itself; it goes in .gitignore.”

Code:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# after adding a package, record exact versions
pip freeze > requirements.txt
Red flag to avoid:

Installing everything globally with sudo pip, or committing the whole environment folder to Git.

They may ask next:
  • How do you check which Python and pip you're actually using?
  • Why pin exact package versions in a requirements file?
Say it in 60 seconds

Built-in Types 4 questions

Easy Technical round Fresher Practice question

7. How does slicing work in Python? What do nums[-1], nums[1:4] and text[::-1] give you?

What the interviewer is really testing:
Whether you are comfortable with the everyday indexing tools that show up in almost every small coding task.
Answer frame:

Negative index: counts from the end, so -1 is the last item.

Start and stop: start is included, stop is not, so 1:4 gives three items.

Step: the third number is the stride; -1 walks backwards and reverses the sequence.

Safety: a slice past the end just stops, while a single index past the end raises IndexError.

Sample spoken answer:

“Indexes start at zero, and negative indexes count from the end, so nums[-1] is the last item. A slice is start, stop and step. The start is included and the stop isn't, so nums[1:4] gives me the items at positions one, two and three. If I leave out start or stop, it means from the beginning or to the end. The step is how far to jump each time, and a step of minus one walks backwards, so text[::-1] reverses a string. Two things I keep in mind: slicing a list gives me a new list, and a slice that runs past the end just stops quietly, while a single index past the end raises an IndexError.”

Code:
nums = [10, 20, 30, 40, 50]
nums[-1]     # 50
nums[1:4]    # [20, 30, 40]
nums[::2]    # [10, 30, 50]
"python"[::-1]  # 'nohtyp'
nums[3:100]  # [40, 50], no error
Red flag to avoid:

Thinking the stop index is included, or that slicing a list changes the original list.

They may ask next:
  • How would you get the last three items of a list, and what if the list has only two?
  • Does copy = nums[:] copy the objects inside the list too?
Say it in 60 seconds
Easy Technical round Fresher Practice question

8. What's the difference between append, extend and insert on a list? What happens if you append one list to another?

What the interviewer is really testing:
Whether you know the list methods well enough to avoid the classic nested-list surprise.
Answer frame:
append vs extend vs insert
appendadds exactly one object at the end, even if that object is a list.
extendloops over an iterable and adds each item; += on a list does the same.
insertputs one item at a given index and shifts the rest, which is slower on big lists.
Sample spoken answer:

“append adds one object to the end. If that object is a list, I get a list inside my list, which surprises people the first time. extend takes any iterable and adds each of its items one by one, so extending with two numbers makes the list two longer. One gotcha is that extending with a string adds each character separately. insert takes an index and an item and puts it at that position, shifting everything after it along, so inserting at the front of a large list is slow compared with appending at the end. If I need to add at the front a lot, I'd use a deque from collections instead.”

Code:
a = [1, 2]
a.append([3, 4])   # [1, 2, [3, 4]]
b = [1, 2]
b.extend([3, 4])   # [1, 2, 3, 4]
b.insert(0, 0)     # [0, 1, 2, 3, 4]
Red flag to avoid:

Saying append and extend do the same thing, or not knowing that appending a list nests it.

They may ask next:
  • What does nums += "ab" do to a list of numbers?
  • What's the difference between nums.extend(other) and nums = nums + other?
Say it in 60 seconds
Easy Technical round Fresher Practice question

9. What's the difference between d[key] and d.get(key)? How would you count how many times each item appears in a list?

What the interviewer is really testing:
Whether you handle missing keys deliberately and know the standard counting patterns.
Answer frame:

Brackets: raise KeyError when the key is missing.

get: returns None, or a default you pass, when the key is missing.

Counting: counts.get(x, 0) + 1, or collections.Counter which does it for you.

Sample spoken answer:

“d[key] gives me the value but raises a KeyError if the key isn't there. d.get(key) returns None instead, or a default I pass as the second argument. I use brackets when a missing key would be a real bug and I want it to fail loudly, and get when missing is normal. For counting, the simple version loops over the items and does counts[item] = counts.get(item, 0) + 1. In real code I'd just use Counter from collections, which does the counting in one line and gives me most_common for free. defaultdict(int) is another option when I'm building counts inside a bigger loop.”

Code:
from collections import Counter

fruits = ["apple", "kiwi", "apple"]
counts = {}
for f in fruits:
    counts[f] = counts.get(f, 0) + 1
# {'apple': 2, 'kiwi': 1}

Counter(fruits).most_common(1)  # [('apple', 2)]
Red flag to avoid:

Wrapping every lookup in try/except without knowing get exists, or saying get raises an error for a missing key.

They may ask next:
  • What does setdefault do?
  • How would you group a list of words by their first letter?
Say it in 60 seconds
Easy Technical round Fresher Practice question

10. What's the difference between list.sort() and sorted()? And what ends up in nums after nums = nums.sort()?

What the interviewer is really testing:
Whether you know which tools change a list in place and which return a new one, the root of many beginner bugs.
Answer frame:
list.sort() vs sorted()
list.sort()sorts the list itself and returns None; only lists have it.
sorted()takes any iterable and returns a new sorted list, leaving the original alone.

Options: both take key and reverse, and both are stable, so equal items keep their order.

Sample spoken answer:

“list.sort() sorts the list itself and returns None. sorted() takes any iterable, like a list, a tuple, a string or a dict's keys, and gives me back a brand new list, leaving the original untouched. The classic bug is writing nums = nums.sort(). The sort happens, but then I overwrite nums with the None that sort returned, so my list is gone. I use sort() when I own the list and don't need the old order, and sorted() when I need to keep the original or I'm sorting something that isn't a list. Both take a key function and reverse=True. Both are also stable, meaning items that compare equal keep their original order, which is handy when I sort by one field and then another.”

Code:
nums = [3, 1, 2]
result = nums.sort()
print(result)   # None
print(nums)     # [1, 2, 3]

words = ("pear", "fig", "apple")
sorted(words, key=len)   # ['fig', 'pear', 'apple']
Red flag to avoid:

Writing nums = nums.sort() and not seeing why the list became None, or thinking sorted() changes the original.

They may ask next:
  • What does sorted() give you back when you pass it a dict?
  • Why does sorting a list that mixes numbers and strings fail in Python 3?
Say it in 60 seconds

Functions and Scope 2 questions

Hard Technical round Fresher, Mid-level Practice question

11. Why does a function that does count += 1 on a global count raise UnboundLocalError? How would you fix it?

What the interviewer is really testing:
Whether you understand how Python decides if a name is local, which separates people who have debugged scope bugs from those who have not.
Answer frame:

The rule: any assignment to a name inside a function makes that name local for the whole function.

The error: count += 1 reads the local count before it has a value.

Lookup order: local, enclosing function, global, then built-ins.

The fix: pass the value in and return it; use global or nonlocal only when you mean it.

Sample spoken answer:

“Python decides at compile time which names in a function are local. If a function assigns to a name anywhere, even with +=, that name is local for the whole function. So count += 1 tries to read the local count before it has been given a value, and I get an UnboundLocalError. When a name isn't assigned in the function, Python looks it up in order: local, then any enclosing function, then the module's globals, then built-ins. To fix it, I'd usually pass the count in and return the new value, which keeps the function easy to test. If I really need to change the module-level variable, I declare global count. Inside a nested function, nonlocal does the same for the outer function's variable.”

Code:
count = 0

def bump():
    count += 1      # UnboundLocalError

def bump_fixed(count):
    return count + 1

count = bump_fixed(count)
Red flag to avoid:

Saying the variable 'doesn't exist yet' without explaining that the assignment makes it local, or reaching for global as the default fix.

They may ask next:
  • What does nonlocal do that global can't?
  • Why can a function read a global list and call .append on it without any keyword?
Say it in 60 seconds
Easy Technical round Fresher Practice question

12. What is a lambda? Show map and filter with one, and tell me when you'd use a comprehension instead.

What the interviewer is really testing:
Whether you know these tools and have taste about readability, not just syntax.
Answer frame:

Lambda: a small unnamed function made of a single expression.

map and filter: apply a function to each item, or keep items where it returns true; both return lazy iterators.

Comprehension: usually clearer for simple transforms; lambdas shine as a key for sorting.

Sample spoken answer:

“A lambda is a small function with no name, written in one expression, like lambda x: x * 2. It can't hold statements or multiple lines. map applies a function to every item, and filter keeps only the items where the function returns something truthy. In Python 3 both give back lazy iterators, so I wrap them in list() if I need the whole result. Honestly, for simple cases I find a list comprehension easier to read, because the logic sits right there, with no lambda. Where lambdas really earn their place is as a key argument, like sorting a list of students by marks. If a lambda gets long or I need it twice, I give it a proper name with def.”

Code:
nums = [1, 2, 3, 4, 5]
doubled = list(map(lambda n: n * 2, nums))
evens = list(filter(lambda n: n % 2 == 0, nums))
evens_too = [n for n in nums if n % 2 == 0]
students = [{"name": "Ana", "marks": 81}, {"name": "Ben", "marks": 92}]
students.sort(key=lambda s: s["marks"], reverse=True)
Red flag to avoid:

Writing long, nested lambdas and calling it clean code, or not knowing that map returns an iterator in Python 3.

They may ask next:
  • Why does printing map(str, nums) not show the values?
  • How would you sort a list of dicts by two fields?
Say it in 60 seconds

Coding Tasks 6 questions

Easy Coding round Fresher Practice question

13. Write a function that checks whether a string is a palindrome, ignoring case, spaces and punctuation.

What the interviewer is really testing:
Whether you can turn a small spec into clean code and think about cleaning the input first.
Answer frame:

Clean: keep only letters and digits, lowercased.

Compare: check the cleaned text against its reverse.

Improve: two pointers from both ends avoid building a reversed copy.

Sample spoken answer:

“First I clean the input, because the spec says spaces and punctuation don't count. I keep only characters where isalnum() is true and lowercase them. Then a palindrome is just text that equals its own reverse, so I compare the cleaned version with a reversed slice. That's linear time and uses extra memory for the cleaned copy. If the interviewer wants less memory, I'd use two pointers, one at each end, skip anything that isn't a letter or digit, and compare as they move inward, stopping at the first mismatch. I'd test it with an empty string, a single character, a sentence like 'A man, a plan, a canal: Panama', and a near-miss.”

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

is_palindrome("A man, a plan, a canal: Panama")  # True
Red flag to avoid:

Forgetting to strip punctuation and spaces, or writing a manual loop to reverse a string without knowing slicing.

They may ask next:
  • How would you find the longest palindrome inside a string?
  • Would casefold() be better than lower() here? Why?
Say it in 60 seconds
Easy Coding round Fresher Practice question

14. Write a function that reverses the order of words in a sentence, so "I love Python" becomes "Python love I". What about extra spaces?

What the interviewer is really testing:
Whether you know the everyday string tools, split and join, and think about messy input like repeated spaces.
Answer frame:

Split: split() with no argument splits on any run of whitespace and drops empty pieces.

Reverse: reverse the list of words with a slice or reversed().

Join: " ".join(...) puts the words back with single spaces, building the string once.

Sample spoken answer:

“I'd split the sentence into words, reverse the list and join it back with spaces. The detail that matters is calling split() with no argument. That splits on any run of spaces, tabs or newlines and ignores whitespace at the ends, so extra spaces don't turn into empty words. If I wrote split(" ") instead, two spaces in a row would give me an empty string in the list. For reversing I use a slice with a step of minus one, then " ".join to glue the words back with single spaces. Strings can't be changed in place, so building a list and joining once is the normal way, rather than adding strings together in a loop. The whole thing is linear in the length of the sentence.”

Code:
def reverse_words(sentence):
    words = sentence.split()
    return " ".join(words[::-1])

reverse_words("  I love   Python ")  # 'Python love I'
"a  b".split(" ")                    # ['a', '', 'b']
Red flag to avoid:

Using split(" ") and returning empty words for repeated spaces, or not knowing what join does.

They may ask next:
  • How would you reverse each word but keep the words in the same order?
  • Could you do it without split, by walking the string yourself?
Say it in 60 seconds
Medium Coding round Fresher Practice question

15. Write a function that finds the earliest non-repeating letter in a word like swiss. Why is calling s.count() inside a loop a poor choice?

What the interviewer is really testing:
Whether you reach for a dictionary to count instead of a nested loop, and can state the complexity.
Answer frame:

Count: one pass to count every character with a dict or Counter.

Find: a second pass over the string returns the first character with count one.

Complexity: linear time; the naive s.count(ch) in a loop is quadratic.

Sample spoken answer:

“The simple approach calls s.count(ch) for every character, but that scans the whole string each time, so it's quadratic. Better is two passes. First I count every character with a Counter. Then I walk the string again in its original order and return the first character whose count is one. If none qualifies, I return None. Both passes are linear, so it's O(n) time, and the extra memory is the number of distinct characters, which is small for normal text. I'd check an empty string, a string where everything repeats, and one where the answer is the last character, and I'd ask whether upper and lower case should count as the same.”

Code:
from collections import Counter

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

first_unique("swiss")  # 'w'
Red flag to avoid:

Using a set to find unique characters and losing the original order, or not being able to say why the nested approach is slow.

They may ask next:
  • How would you change it for a stream of characters where you can't go back?
  • What if the answer should ignore case?
Say it in 60 seconds
Easy Coding round Fresher Practice question

16. Remove duplicates from a list but keep the original order. Why doesn't list(set(items)) do the job?

What the interviewer is really testing:
Whether you know sets have no reliable order and can keep order with a seen-set or dict.
Answer frame:

The problem: a set drops duplicates but doesn't keep the order items came in.

Loop version: keep a seen set and append each item the first time you meet it.

Short version: list(dict.fromkeys(items)), since dicts keep insertion order.

Sample spoken answer:

“Turning a list into a set does remove duplicates, but a set makes no promise about order, so the result can come back shuffled. To keep the order, I loop through the list with a seen set. For each item, if it's not in seen, I add it to the set and append it to the result. Checking a set is fast on average, so the whole thing is linear. There's also a neat one-liner, list(dict.fromkeys(items)), which works because dicts keep insertion order in modern Python. Both need the items to be hashable, so for a list of lists I'd convert each inner list to a tuple first.”

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

dedupe([3, 1, 3, 2, 1])  # [3, 1, 2]
Red flag to avoid:

Checking if x not in result against a list and not noticing it makes the solution quadratic.

They may ask next:
  • What would you do if the items were dicts, which aren't hashable?
  • Why is checking x in seen faster for a set than for a list?
Say it in 60 seconds
Medium Coding round Fresher Practice question

17. Find the second largest number in a list without sorting it. What should happen with duplicates or a list of one?

What the interviewer is really testing:
Whether you can write a single-pass solution and think about edge cases before being asked.
Answer frame:

Clarify: second largest distinct value? What to return for fewer than two distinct numbers?

One pass: track the largest and second largest as you go.

Update rule: a new largest pushes the old largest down to second; skip values equal to the largest.

Sample spoken answer:

“First I'd ask two things: do we mean the second largest distinct value, and what should come back if there isn't one. I'll assume distinct and return None. Then one pass is enough. I keep first and second, both starting as None. For each number, if it's bigger than first, the old first becomes second and the new number becomes first. Otherwise, if it's not equal to first and bigger than second, it becomes the new second. That's O(n) time and constant memory, better than sorting's O(n log n). I'd test with duplicates of the max, a single item, a list that's all the same number, and negative numbers.”

Code:
def second_largest(nums):
    first = second = None
    for n in nums:
        if first is None or n > first:
            first, second = n, first
        elif n != first and (second is None or n > second):
            second = n
    return second

second_largest([5, 5, 3, 4])  # 4
Red flag to avoid:

Starting both trackers at zero, which breaks on lists of negative numbers, or ignoring duplicates of the largest value.

They may ask next:
  • How would you find the k-th largest value efficiently?
  • Why is starting first and second at zero a bug?
Say it in 60 seconds
Medium Coding round Fresher, Mid-level Practice question

18. Given a list of numbers and a target, return the indexes of two numbers that add up to the target. Can you beat checking every pair?

What the interviewer is really testing:
Whether you can move from the brute-force idea to a hash-based one and explain the trade-off.
Answer frame:

Brute force: check every pair; quadratic time, no extra memory.

Better: walk once, storing each number's index in a dict.

Check first: before storing a number, look up target - n so one item isn't used twice.

Sample spoken answer:

“The first idea is two nested loops that try every pair. It works but it's quadratic, so it gets slow on big lists. The better way uses a dict from number to index. I walk the list once. For each number, I work out what I still need, which is the target minus the number. If that value is already in the dict, I've found my pair and return both indexes. If not, I store the current number with its index and move on. Checking before storing means I never pair a number with itself, and it still works when the same value appears twice. That's O(n) time, and I pay for it with O(n) extra memory for the dict.”

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

two_sum([2, 7, 11, 15], 9)  # (0, 1)
Red flag to avoid:

Storing the number before checking, which pairs an item with itself, or not being able to say what the dict costs in memory.

They may ask next:
  • How would you solve it if the list were already sorted and you couldn't use extra memory?
  • What changes if you need every pair, not just one?
Say it in 60 seconds

OOP Basics 2 questions

Hard Technical round Fresher, Mid-level Practice question

19. What are self and __init__ for? And what goes wrong if a class keeps a list as a class attribute?

What the interviewer is really testing:
Whether you understand how instances and classes hold data, including the shared-list bug that catches many beginners.
Answer frame:

self: the instance the method was called on, passed in automatically as the first argument.

__init__: sets up a new object's starting data right after the object is created.

Class attribute: one value shared by every instance; a shared list collects everyone's items.

Fix: create the list inside __init__ so each object gets its own.

Sample spoken answer:

“self is the object the method was called on. When I write cart.add(item), Python passes cart in as self for me. __init__ runs right after a new object is created and sets its starting data, like self.owner = owner. The trap is putting a mutable value on the class itself. If I write items = [] in the class body, there's only one list, owned by the class, and every instance that calls self.items.append adds to that same list. So adding a pen to one cart makes it show up in every cart. The fix is to create the list inside __init__, so each object gets its own. Class attributes are fine for constants that everyone should share.”

Code:
class Cart:
    items = []            # one list shared by every Cart
    def add(self, item):
        self.items.append(item)

a, b = Cart(), Cart()
a.add("pen")
print(b.items)            # ['pen']

class FixedCart:
    def __init__(self):
        self.items = []   # each cart gets its own list
Red flag to avoid:

Calling self a keyword, or saying class attributes are copied into each instance.

They may ask next:
  • What happens if one instance does self.items = [] instead of appending?
  • What's the difference between __init__ and __new__?
Say it in 60 seconds
Medium Technical round Fresher Practice question

20. Python has no private keyword. What do a single leading underscore and a double leading underscore on an attribute mean?

What the interviewer is really testing:
Whether you know how Python handles encapsulation by convention, and what name mangling really is.
Answer frame:

Single underscore: a convention meaning 'internal, don't rely on this'; nothing stops access.

Double underscore: triggers name mangling to _ClassName__attr, mainly to avoid clashes in subclasses.

Dunder names: names with underscores on both sides are special methods, not private ones.

Controlled access: use @property when you want checks around reading or setting a value.

Sample spoken answer:

“Python relies on convention rather than enforcement. A single leading underscore, like _balance, tells other developers it's internal and might change, but anyone can still read it. It also keeps the name out of from module import * when the module doesn't define __all__. A double leading underscore, like __balance, makes Python rename it inside the class to _Account__balance. That's called name mangling. Its real purpose is to stop a subclass from accidentally overwriting the parent's attribute, not to hide data, because you can still reach the mangled name. Names with double underscores on both sides, like __init__, are special methods and aren't mangled. When I need rules around a value, I use a @property.”

Red flag to avoid:

Claiming a double underscore makes an attribute truly private or impossible to access.

They may ask next:
  • How would you use @property to stop a balance being set to a negative number?
  • Why is name mangling not a security feature?
Say it in 60 seconds

Files and Errors 2 questions

Easy Technical round Fresher Practice question

21. How do you read a text file line by line in Python, and what do the modes r, w and a do?

What the interviewer is really testing:
Whether you can do basic file work safely without loading everything into memory or wiping a file by accident.
Answer frame:

Open safely: use with open(...) so the file closes even on an error, and pass an encoding.

Line by line: loop over the file object itself; it reads lazily.

Modes: r reads and fails if missing, w creates or empties the file, a creates or adds to the end.

Sample spoken answer:

“I open the file in a with block so it gets closed even if something goes wrong, and I pass encoding="utf-8" because the default depends on the machine. Then I loop over the file object directly. That reads one line at a time, so it's fine even for a big file, unlike read() or readlines(), which pull everything into memory. Each line keeps its newline, so I usually strip it. For modes: r is read, the default, and it raises FileNotFoundError if the file isn't there. w is write, which creates the file or empties an existing one, so it's easy to wipe data by mistake. a is append, which creates the file if needed and adds to the end.”

Code:
with open("marks.txt", "r", encoding="utf-8") as f:
    for line in f:
        name, score = line.rstrip("\n").split(",")
        print(name, int(score))

with open("log.txt", "a", encoding="utf-8") as log:
    log.write("done\n")
Red flag to avoid:

Using w to add to a log file and wiping it, or reading a huge file with read() in one go.

They may ask next:
  • Which mode would you use to create a file only if it doesn't already exist?
  • When do you need to open a file in binary mode?
Say it in 60 seconds
Medium Technical round Fresher Practice question

22. How would you define and raise your own exception for bad input, and why not just raise a plain Exception?

What the interviewer is really testing:
Whether you can design errors that callers can handle precisely, not just print a message.
Answer frame:

Define: a small class that inherits from a fitting built-in, such as ValueError.

Raise: with a clear message that includes the bad value.

Why: callers can catch exactly this error without swallowing unrelated ones.

Sample spoken answer:

“I'd define a small class, say InvalidAgeError, that inherits from ValueError, because bad input is a value problem. The body can just be pass. In my function, when the input is wrong, I raise InvalidAgeError with a message that says what was expected and what I got. The reason not to raise a plain Exception is that the caller can only catch it with except Exception, which also catches every other bug, like a typo that causes a NameError. With a specific class, the caller catches exactly that case and lets real bugs surface. Inheriting from ValueError also means code that already handles ValueError keeps working. If I'm turning one error into another, I use raise ... from err to keep the original cause.”

Code:
class InvalidAgeError(ValueError):
    pass

def set_age(age):
    if age < 0:
        raise InvalidAgeError(f"age must not be negative, got {age}")
    return age

try:
    set_age(-4)
except InvalidAgeError as err:
    print("Please fix:", err)
Red flag to avoid:

Raising bare Exception everywhere, or returning error strings like 'invalid' instead of raising.

They may ask next:
  • What does raise NewError(...) from err add to the traceback?
  • When would you create a base exception class for a whole module?
Say it in 60 seconds

Projects and Learning 3 questions

Easy Behavioral round Fresher Practice question

23. Pick one Python project from your resume. Which lines of it did you write, what broke along the way, and how would you build it today?

What the interviewer is really testing:
Whether you really wrote the code you list on your resume and can reflect honestly on its weak spots.
Answer frame:

What it did: one line on the problem and who used it.

Your part: the pieces you wrote yourself, and one decision you made.

A problem: something that broke and how you fixed it.

Hindsight: one concrete thing you'd change now.

Sample spoken answer:

“In my final year, three of us built a tool that took exam marks from spreadsheets exported as CSV files, cleaned them and produced a report card for each student. My part was the reading and checking code. I wrote functions that parsed each row, flagged missing marks and caught names typed in different ways. Halfway through, one department's file used a different date format and the whole run crashed. I fixed it by validating each row and collecting the bad rows into an error file instead of stopping. I also added a few pytest tests after that scare. If I did it again, I'd use Git from day one, since we lost an afternoon merging copies by hand, and I'd write the tests before the code, not after.”

Red flag to avoid:

Describing the team's project in 'we' terms only, so the interviewer can't tell what you personally wrote.

They may ask next:
  • Which part of that code are you least proud of?
  • How did you split the work in your team?
Say it in 60 seconds
Medium Behavioral round Fresher Practice question

24. Tell me about a time you were stuck on a bug in an assignment or project for a long time. How did you finally get unstuck?

What the interviewer is really testing:
Whether you have a real debugging method and know when to ask for help, rather than guessing at random.
Answer frame:

The bug: what you saw, in one sentence.

Your method: how you narrowed it down step by step.

The cause: what it turned out to be.

The habit: what you do differently since.

Sample spoken answer:

“In a data structures lab, my recursive function to search a binary tree kept returning None, even for values I could see were in the tree. I spent an hour changing things at random, which didn't help. Then I slowed down. I made the smallest tree that failed, just three nodes, and used breakpoint() to step through each call. I saw the function found the value deep down, but the result vanished on the way back up. I'd written the recursive call without return in front of it, so the found value was thrown away. After that, I always start with the smallest input that fails and step through it before changing code, and I set myself a time limit before asking a classmate or the lab assistant.”

Red flag to avoid:

A story with no method, just 'I kept trying until it worked', or one where someone else found the bug.

They may ask next:
  • What do you do when you can't reproduce a bug on your own machine?
  • How long do you try alone before asking someone?
Say it in 60 seconds
Medium Situational round Fresher Practice question

25. In your first week, you're asked to fix a small bug in a large Python codebase you've never seen. How do you start?

What the interviewer is really testing:
Whether you can work in real, unfamiliar code in an orderly way and ask good questions at the right time.
Answer frame:

Run it: set up the environment and get the project and its tests running locally.

Reproduce: make the bug happen and read the traceback from the bottom up.

Locate: search the code for the error message or function name; read only what you need.

Fix safely: add a test that fails, make the smallest change, run the tests, then ask for review.

Sample spoken answer:

“First I'd get the project running on my machine: create a virtual environment, install the requirements and run the existing tests, so I know what normal looks like. Then I'd reproduce the bug myself. If there's a traceback, I read it from the bottom up to find the exact line. I'd search the codebase for that function or the error message instead of trying to understand everything at once. Before changing anything, I'd write a small test that fails because of the bug. Then I make the smallest fix that makes it pass and run the full test suite. If I'm still stuck after an hour or so, I ask a teammate with what I've tried, not just 'it doesn't work'.”

Red flag to avoid:

Planning to read the whole codebase first, or rewriting a large chunk to fix a small bug.

They may ask next:
  • What would you do if the project has no tests at all?
  • How do you decide the fix is small enough, and not a rewrite?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

You practiced these. On the real call, ClapAssist helps with the rest.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your live interview audio and screen are never stored. Your resume and notes are saved to your account so the app fills them in on any computer. It stays out of screen share on every plan, including Free; only you can see it.

Download with 10 free minutes
Mac and Windows · Stays out of screen share · No card