MyInternships.in
TOP 100 QUESTIONS · WITH ANSWERS & CODE

Python Developer Interview Questions and Answers for Freshers

The 100 most-asked Python interview questions for freshers and entry-level developers — each with a clear model answer, an explanation of whyit's the right answer, and code where it helps. Covers everything from basics and data structures to OOP, decorators, generators and live coding rounds.

Which interviews are these questions helpful for?

This guide is built for freshers, final-year students and entry-level candidatespreparing for Python developer, backend developer, software engineer, automation and data roles. The questions mirror the format used in real technical interviews and campus/off-campus drives at India's top employers, including:

  • Service MNCs — TCS, Infosys, Wipro, Accenture, Cognizant, Capgemini, HCLTech, Tech Mahindra, IBM
  • Consulting & GCCs — Deloitte, EY, ZS, Optum, Publicis Sapient
  • Product companies & startups — for Python/backend SDE-1 roles
  • Campus placements, walk-ins, and internship-to-FTE interviews

You'll also find these useful for online assessments (HackerRank/HackerEarth), the technical round, and viva/aptitude+coding rounds. Pair this with our free Python tutorial to revise any concept in depth, and the Python interview questions hub for more topic-wise sets.

What's covered

Python Basics 9Data Types 5Operators 3Memory 5Scope 1Data Structures 14Comprehensions 2Strings 4Functions 8OOP 12Advanced 9Concurrency 2Exceptions 3Modules 3Coding 12Output Prediction 3Libraries 4Practical 1
1
Python BasicsEasy

What is Python? What are its key features and why is it so popular in the industry?

Answer: Python is a high-level, general-purpose, interpreted programming language known for its simple, readable syntax. It is dynamically typed, supports multiple paradigms (procedural, object-oriented, functional), and comes with a huge standard library plus a massive third-party ecosystem (PyPI).

Explanation: Interviewers want to hear specific reasons: easy to learn/read, huge community and libraries (NumPy, Django, TensorFlow), cross-platform, great for automation/scripting/AI/web/data, and rapid prototyping. Mentioning "batteries included" (rich standard library) is a strong signal of real knowledge.

2
Python BasicsMedium

Is Python an interpreted or a compiled language? Explain what happens to your .py file when you run it.

Answer: Python is technically both: the source code (.py) is first compiled to platform-independent bytecode (.pyc), and that bytecode is then executed line-by-line by the Python Virtual Machine (PVM), which is why it "feels" interpreted.

Explanation: A common gotcha: candidates say "purely interpreted," which is imprecise. CPython compiles to bytecode automatically and caches it in a __pycache__ folder so subsequent runs skip recompilation if the source hasn't changed.

Python
# Running a script compiles it internally
python3 my_script.py

# The compiled bytecode is cached here:
# __pycache__/my_script.cpython-311.pyc
3
Python BasicsEasy

Python is described as "dynamically typed" — what does that actually mean?

Answer: It means the type of a variable is determined at runtime based on the value assigned to it, not declared in advance, and the same variable name can be rebound to a value of a different type later. This is different from statically typed languages like Java or C++, where the type is fixed at compile time.

Explanation: Dynamic typing is not the same as "weak typing." Python is dynamically but strongly typed — it won't silently convert an int and a str in an addition; it raises a TypeError instead.

Python
x = 10        # x is an int
x = "hello"   # now x is a str, no error
x = 10 + "5"  # TypeError: strong typing still applies
4
Data TypesEasy

What is the difference between a list and a tuple in Python?

Answer: A list is mutable (can be changed after creation) and written with square brackets, while a tuple is immutable (cannot be changed after creation) and written with parentheses. Because tuples are immutable, they are generally faster to iterate over, use less memory, and can be used as dictionary keys or set elements, unlike lists.

Explanation: Interviewers often follow up asking "when would you use a tuple over a list?" — good answer: for fixed collections of heterogeneous data (like coordinates or a database row) or when you want to guarantee the data won't be accidentally modified.

Python
my_list = [1, 2, 3]
my_list.append(4)      # works fine

my_tuple = (1, 2, 3)
my_tuple.append(4)     # AttributeError: no append on tuple
5
Data TypesEasy

What are the built-in data types in Python?

Answer: Python groups built-in types into: numeric (int, float, complex), sequence (str, list, tuple, range), mapping (dict), set types (set, frozenset), boolean (bool), binary (bytes, bytearray, memoryview), and NoneType (None).

Explanation: Freshers often forget bytes/bytearray/frozenset when asked "name all of them" — mentioning these shows breadth. Also good to know: everything in Python, including these types, is an object.

6
OperatorsMedium

What is the difference between == and is in Python?

Answer: == checks value equality (whether two objects hold the same data), while is checks identity equality (whether two references point to the exact same object in memory). Two different objects can have equal values (==) but not be the same object (is).

Explanation: A classic trap: small integers (-5 to 256) and short strings are cached/interned by CPython, so "is" may return True by implementation detail even for value comparison — this should never be relied upon. Always use == for value comparison and is only for identity (e.g., checking against None).

Python
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)   # True  (same values)
print(a is b)   # False (different objects in memory)

c = a
print(a is c)   # True  (same object)
7
Python BasicsEasy

What is PEP 8 and why does it matter in a professional codebase?

Answer: PEP 8 is Python's official style guide, covering naming conventions, indentation (4 spaces), line length, imports, and whitespace rules. Following it matters because it keeps code readable and consistent across teams, which is critical when many developers collaborate on the same codebase.

Explanation: A good fresher answer mentions tools that enforce it in real companies: flake8, pylint, black, or ruff, often wired into CI/CD or pre-commit hooks. This shows awareness of real-world engineering practice, not just theory.

8
MemoryMedium

How does Python manage memory internally?

Answer: Python uses automatic memory management via a private heap. Every object has a reference count, and when that count drops to zero, the memory is immediately freed. On top of reference counting, Python has a cyclic garbage collector (the gc module) that periodically detects and cleans up reference cycles that reference counting alone cannot free.

Explanation: A strong answer distinguishes the two mechanisms: reference counting (immediate, deterministic) vs. generational garbage collection (handles cycles, e.g., two objects referencing each other). Mentioning sys.getrefcount() or the gc module is a bonus.

Python
import sys
a = []
print(sys.getrefcount(a))  # reference count of 'a'
9
Data TypesEasy

What is the difference between mutable and immutable data types? Give examples of each.

Answer: Mutable objects can be changed in place after creation without changing their identity (id()) — examples: list, dict, set. Immutable objects cannot be changed after creation; any "modification" actually creates a new object — examples: int, float, str, tuple, frozenset, bool.

Explanation: A common interview follow-up: "why does this matter?" — mutable default arguments in function definitions are a classic Python bug, and immutable objects are safe to use as dictionary keys or in multithreaded code since they can't be changed unexpectedly.

Python
s = "hello"
s[0] = "H"     # TypeError: str is immutable
s = "H" + s[1:] # creates a NEW string object instead

lst = [1, 2, 3]
lst[0] = 99    # works fine, list is mutable, same object
10
OperatorsMedium

What is the difference between /, // and % operators in Python?

Answer: / is true division and always returns a float. // is floor division and returns the largest integer less than or equal to the result (an int if both operands are ints). % is the modulo operator and returns the remainder of the division.

Explanation: Watch out for negative numbers: floor division rounds toward negative infinity, not toward zero, so -7 // 2 is -4, not -3. This is a favorite "predict the output" trap question at product companies.

Python
print(7 / 2)    # 3.5
print(7 // 2)   # 3
print(7 % 2)    # 1
print(-7 // 2)  # -4  (rounds toward -infinity, not -3)
print(-7 % 2)   # 1   (sign follows the divisor)

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →
11
Data TypesMedium

Does Python have integer overflow? How does it handle very large integers?

Answer: No, Python integers have arbitrary precision — they automatically grow to accommodate any size limited only by available memory, so there is no fixed-width overflow like in C or Java. Python internally switches representation as needed without the programmer doing anything special.

Explanation: This is a common "gotcha" question because candidates coming from C/Java backgrounds assume ints wrap around at a fixed bit width (like 32-bit overflow). In Python 3, int is unified (there is no separate "long" type as in Python 2) and scales automatically.

Python
big = 2 ** 1000   # perfectly valid, no overflow
print(big)        # prints a very long number correctly
12
ScopeMedium

What are namespaces in Python, and can you explain the LEGB rule?

Answer: A namespace is a mapping from names to objects (like a dictionary) that ensures names are unique and don't clash across different scopes. LEGB describes the order Python searches for a name: Local (current function) -> Enclosing (any enclosing function) -> Global (module level) -> Built-in (Python's built-in names like len, print).

Explanation: Interviewers often ask this alongside a code snippet using nested functions to test if you understand closures and variable shadowing. Knowing that the search stops at the first match found (in LEGB order) is key.

Python
x = "global"

def outer():
    x = "enclosing"
    def inner():
        x = "local"
        print(x)   # "local" -> Local scope wins first
    inner()

outer()
13
OperatorsMedium

Why is "if x is None" preferred over "if x == None" in Python?

Answer: None is a singleton — there is only ever one None object in a running Python program — so "is None" checks identity, which is faster and semantically correct for checking "this is literally the None object." Using == None instead relies on the __eq__ method, which could theoretically be overridden by a custom class to behave unexpectedly.

Explanation: This is both a style (PEP 8 explicitly recommends "is None") and a correctness question. A class could override __eq__ so that obj == None returns True even when obj is not actually None, causing subtle bugs; "is None" can never be fooled this way.

Python
class Weird:
    def __eq__(self, other):
        return True   # dangerous override

w = Weird()
print(w == None)   # True  (misleading!)
print(w is None)   # False (correct)
14
MemoryEasy

What does the id() function do in Python?

Answer: id() returns the unique identity of an object as an integer, which is guaranteed to be constant for that object during its lifetime and unique among simultaneously existing objects. In CPython specifically, this identity value corresponds to the object's memory address.

Explanation: A good follow-up point: the "is" operator internally compares id(a) == id(b). Also worth knowing that after an object is garbage collected, its id can be reused by a new object — id uniqueness is only guaranteed for objects alive at the same time.

Python
a = [1, 2, 3]
b = a
print(id(a) == id(b))  # True, same object
print(a is b)           # True, equivalent check
15
Python BasicsEasy

What are truthy and falsy values in Python? Name the standard falsy values.

Answer: In a boolean context (like an if statement), every Python object is evaluated as either "truthy" or "falsy." The standard falsy values are: False, None, 0, 0.0, 0j, empty sequences/collections ('', [], (), {}, set()), and any object whose __bool__ returns False or __len__ returns 0. Everything else is truthy.

Explanation: A frequent "predict the output" trap: candidates forget that empty collections are falsy but a collection with one falsy element (e.g. [0]) is still truthy, since the list itself is non-empty.

Python
if []:
    print("truthy")
else:
    print("falsy")   # prints this - empty list is falsy

if [0]:
    print("truthy")  # prints this - list has 1 item, non-empty
16
Python BasicsEasy

What are the main differences between Python 2 and Python 3?

Answer: Python 3 made print a function instead of a statement (print("x") vs print "x"), division of two ints with / returns a float by default (true division), strings are Unicode by default instead of ASCII, and the range() function returns an iterator/view instead of a list. Python 2 reached end-of-life in January 2020, so all modern development targets Python 3.

Explanation: Freshers should confidently state that Python 2 is dead/unsupported and that all new projects, libraries, and companies use Python 3.10+. Bonus points for mentioning integer division (/ vs //) and the unified int type in Python 3 (no separate "long").

Python
# Python 2: print "hello"        -> Python 3: print("hello")
# Python 2: 5 / 2 == 2            -> Python 3: 5 / 2 == 2.5
# Python 2: range() returns list  -> Python 3: range() returns range object
17
Python BasicsEasy

What are keywords in Python? Can you name a few, and can they be used as variable names?

Answer: Keywords are reserved words that have special meaning to the Python interpreter and are part of the language's syntax — examples include if, else, for, while, def, class, import, return, True, False, None, and, or, not, lambda. They cannot be used as identifiers (variable, function, or class names) because that would cause a SyntaxError.

Explanation: A sharp answer mentions how to check the full list programmatically using the keyword module, which shows practical, hands-on Python knowledge rather than rote memorization.

Python
import keyword
print(keyword.kwlist)   # full list of all reserved keywords
print(keyword.iskeyword("class"))  # True

class = 5   # SyntaxError: invalid syntax
18
Data TypesEasy

What is type conversion in Python? Explain implicit vs explicit conversion.

Answer: Type conversion is changing a value from one data type to another. Implicit conversion (coercion) is done automatically by Python, such as when adding an int and a float, Python automatically converts the int to a float. Explicit conversion (casting) is done manually by the programmer using functions like int(), float(), str(), or list().

Explanation: Interviewers like to test edge cases here, like int("12.5") raising a ValueError (you must go through float() first) or int("abc") always failing. Showing awareness of these failure cases is a strong signal.

Python
# Implicit
result = 5 + 2.0     # int auto-converted to float -> 7.0

# Explicit
x = int("42")        # 42
y = float("3.14")    # 3.14
z = int("12.5")      # ValueError! must do int(float("12.5"))
19
Python BasicsEasy

How do you take user input and print output in Python? Explain the sep and end parameters of print().

Answer: input() reads a line from standard input and always returns it as a string (you must cast it explicitly to int/float if needed). print() writes to standard output, and it accepts a sep parameter (string inserted between multiple arguments, default a space) and an end parameter (string appended after the output, default a newline).

Explanation: A common fresher mistake is forgetting input() always returns a str, leading to bugs like age = input("Age: "); age + 1 raising a TypeError. Knowing sep/end shows familiarity with formatting output cleanly, useful in coding-round questions.

Python
age = int(input("Enter age: "))   # must cast explicitly
print("a", "b", "c", sep="-", end="!\n")
# Output: a-b-c!
20
Python BasicsMedium

What is the difference between a statement and an expression in Python?

Answer: An expression is any piece of code that evaluates to a value, like 5 + 3 or x == 2. A statement is a complete line/instruction that performs an action and does not necessarily produce a value, such as an assignment (x = 5), an if block, a for loop, or a function definition (def).

Explanation: A neat way to distinguish them in an interview: expressions can be part of a larger statement (like the right side of an assignment), but a statement cannot be nested inside an expression. Assignment expressions (the walrus operator :=, added in Python 3.8) are a notable exception that let assignment happen as part of an expression.

Python
x = 5 + 3        # "5 + 3" is an expression, the whole line is a statement

if (n := 10) > 5: # walrus operator: assignment used as an expression
    print(n)      # 10

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →

Free Python Tutorial · 100 Lessons

Weak on a topic above? Revise it in minutes.

Our free, beginner-to-advanced Python tutorial covers every concept in these questions — variables, data structures, OOP, decorators, generators and more — with examples and a 30-day learning plan.

21
Data StructuresEasy

How do you remove duplicates from a list in Python?

Answer: The most common way is to convert the list to a set, since sets only store unique elements, and then convert it back to a list. If you need to preserve the original order, use dict.fromkeys() instead, because regular sets do not guarantee order.

Explanation: set() is fast (O(n) average) but does not preserve insertion order reliably for this purpose in interview terms. dict.fromkeys(lst) preserves order because dictionaries maintain insertion order since Python 3.7.

Python
nums = [1, 2, 2, 3, 4, 4, 5]

# order not guaranteed to match input logically speaking
unique_set = list(set(nums))

# preserves original order (Python 3.7+)
unique_ordered = list(dict.fromkeys(nums))
print(unique_ordered)  # [1, 2, 3, 4, 5]
22
Data StructuresEasy

What is the difference between a list and a set in Python?

Answer: A list is an ordered, mutable collection that allows duplicate elements and is accessed by index. A set is an unordered, mutable collection that only stores unique elements and has no indexing. Membership testing (in) is much faster in a set, roughly O(1) on average, compared to O(n) for a list.

Explanation: Use a list when order and duplicates matter (e.g., a sequence of steps). Use a set when you need fast lookups and uniqueness, such as removing duplicates or checking membership in large data.

Python
my_list = [1, 2, 2, 3]
my_set = {1, 2, 3}

print(3 in my_list)  # O(n) scan
print(3 in my_set)   # O(1) average lookup
23
Data StructuresEasy

What is a dictionary in Python? How are keys stored, and are dictionaries ordered?

Answer: A dictionary is a mutable collection of key-value pairs where keys must be unique and hashable. Internally, Python implements dictionaries as hash tables, so each key is stored based on its hash value for fast O(1) average lookup. Since Python 3.7, dictionaries officially preserve insertion order.

Explanation: Before 3.7, ordering was a CPython implementation detail; from 3.7 onward it is a guaranteed language feature. Keys must be hashable, so lists cannot be used as keys, but tuples can.

Python
d = {"b": 2, "a": 1, "c": 3}
print(list(d.keys()))   # ['b', 'a', 'c'] - insertion order preserved
print(d["a"])            # 1, O(1) average lookup
24
Data StructuresEasy

How do you merge two dictionaries in Python?

Answer: In Python 3.9+, you can use the merge operator | to combine two dictionaries into a new one. You can also use the update() method to merge one dictionary into another in place, or unpack both with ** inside a new dictionary literal.

Explanation: With |, if the same key exists in both dictionaries, the value from the right-hand dictionary wins. update() modifies the calling dictionary directly and returns None.

Python
d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 3}

merged1 = d1 | d2                # Python 3.9+, {'a': 1, 'b': 20, 'c': 3}
merged2 = {**d1, **d2}           # works in older Python 3 too

d1.update(d2)                    # modifies d1 in place
print(d1)
25
ComprehensionsEasy

What is list comprehension? Give an example and compare it to a normal for loop.

Answer: List comprehension is a concise, single-line syntax for creating a new list by applying an expression to each item of an iterable, optionally filtered by a condition. It is generally faster and more readable than writing an equivalent explicit for loop with append() calls.

Explanation: Under the hood, list comprehensions still loop, but they avoid repeated attribute lookups for append() and are optimized by the interpreter, making them slightly more performant for simple transformations.

Python
# using a for loop
squares = []
for n in range(10):
    if n % 2 == 0:
        squares.append(n * n)

# equivalent list comprehension
squares = [n * n for n in range(10) if n % 2 == 0]
print(squares)  # [0, 4, 16, 36, 64]
26
ComprehensionsEasy

What is dictionary comprehension? Give an example.

Answer: Dictionary comprehension is a concise syntax for building a dictionary from an iterable in a single expression, similar to list comprehension but producing key-value pairs. It follows the pattern {key_expr: value_expr for item in iterable}.

Explanation: It is commonly used to transform an existing dictionary, build lookup tables, or invert keys and values, and it can also include an if condition to filter items.

Python
nums = [1, 2, 3, 4, 5]

# map each number to its square
squares = {n: n * n for n in nums}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# filter with a condition
even_squares = {n: n * n for n in nums if n % 2 == 0}
print(even_squares)  # {2: 4, 4: 16}
27
StringsMedium

What is slicing in Python? Explain seq[start:stop:step] and how to reverse a list using it.

Answer: Slicing extracts a portion of a sequence such as a list, string, or tuple using the syntax seq[start:stop:step]. start is inclusive, stop is exclusive, and step controls the interval between elements; step defaults to 1. Negative indices count from the end, and a negative step of -1 reverses the sequence.

Explanation: Slicing never raises an IndexError even if start or stop is out of range, and it always returns a new sequence rather than modifying the original.

Python
nums = [10, 20, 30, 40, 50]

print(nums[1:4])     # [20, 30, 40]
print(nums[-2:])     # [40, 50], last two elements
print(nums[::2])     # [10, 30, 50], every second element
print(nums[::-1])    # [50, 40, 30, 20, 10], reversed
28
StringsEasy

How do you reverse a string in Python?

Answer: The simplest way is slicing with a step of -1, such as s[::-1]. You can also use the built-in reversed() function, which returns an iterator, and then join the characters back into a string with "".join().

Explanation: Since strings are immutable, none of these approaches modify the original string; they all create a new string object.

Python
s = "python"

reversed_slice = s[::-1]
print(reversed_slice)  # nohtyp

reversed_join = "".join(reversed(s))
print(reversed_join)   # nohtyp
29
StringsMedium

Are strings mutable in Python? What happens if you concatenate strings in a loop, and how do you avoid it?

Answer: No, strings are immutable in Python; every operation that appears to modify a string actually creates a new string object. Concatenating strings inside a loop using += repeatedly creates new string objects each time, which is inefficient for large loops, roughly O(n^2) in the worst case. The recommended fix is to collect pieces in a list and use "".join() at the end.

Explanation: CPython has some internal optimizations for += on strings in certain cases, but relying on join() is the standard, portable, and interview-expected best practice.

Python
# inefficient: creates a new string object each iteration
result = ""
for word in ["I", "love", "Python"]:
    result += word + " "

# efficient: build a list, then join once
parts = ["I", "love", "Python"]
result = " ".join(parts)
print(result)  # I love Python
30
StringsEasy

What are some common string methods in Python? Give examples of strip, split, join, replace, and find.

Answer: strip() removes leading and trailing whitespace (or given characters), split() breaks a string into a list of substrings based on a delimiter, join() combines an iterable of strings into one string using a separator, replace() substitutes occurrences of a substring, and find() returns the index of the first occurrence of a substring or -1 if not found.

Explanation: find() returns -1 on failure instead of raising an exception, whereas the similar index() method raises a ValueError if the substring is not found.

Python
s = "  Hello, World  "

print(s.strip())                     # "Hello, World"
print(s.strip().split(", "))         # ['Hello', 'World']
print("-".join(["a", "b", "c"]))     # "a-b-c"
print(s.replace("World", "Python"))  # "  Hello, Python  "
print(s.find("World"))               # 9
print(s.find("xyz"))                 # -1

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →
31
Data StructuresEasy

What is the difference between append() and extend() on a list?

Answer: append() adds a single element to the end of a list, even if that element is itself a list or tuple, so it gets nested as one item. extend() takes an iterable and adds each of its elements individually to the end of the list, effectively flattening it by one level.

Explanation: A common bug is calling list1.append(list2) when the intent was to merge two lists; that adds list2 as a single nested element instead of merging its contents.

Python
a = [1, 2, 3]
a.append([4, 5])
print(a)  # [1, 2, 3, [4, 5]]

b = [1, 2, 3]
b.extend([4, 5])
print(b)  # [1, 2, 3, 4, 5]
32
Data StructuresEasy

What is the difference between remove(), pop(), and del on a list?

Answer: remove(value) deletes the first occurrence of a given value from the list and raises a ValueError if the value is not found. pop(index) removes and returns the element at a given index, defaulting to the last element if no index is given. del list[index] removes an element (or a slice) at a given position but does not return the removed value.

Explanation: pop() is the only one that gives you the removed value back, which is useful for stack-like operations. del can also delete an entire list or a slice of it, unlike remove() or pop().

Python
nums = [10, 20, 30, 40]

nums.remove(20)     # removes value 20 -> [10, 30, 40]
last = nums.pop()   # removes and returns 40 -> [10, 30]
del nums[0]         # removes index 0, no return value -> [30]
print(nums, last)
33
Data StructuresEasy

What is tuple unpacking? How do you swap two variables without using a temporary variable?

Answer: Tuple unpacking lets you assign the elements of a tuple (or any iterable) to multiple variables in a single statement, based on their position. Python evaluates the right-hand side as a tuple first and then unpacks it, which allows swapping two variables in one line without a temporary variable, since a, b = b, a assigns the original values simultaneously.

Explanation: This works because the right-hand side tuple (b, a) is fully constructed with the old values before any assignment happens, avoiding the classic three-line swap using a temp variable.

Python
a, b = 1, 2
a, b = b, a
print(a, b)  # 2 1

point = (10, 20, 30)
x, y, z = point
print(x, y, z)  # 10 20 30
34
Data StructuresMedium

What set operations does Python support? Explain union, intersection, and difference.

Answer: Python sets support union (all elements from both sets, using | or union()), intersection (only elements common to both sets, using & or intersection()), and difference (elements in the first set but not the second, using - or difference()). These operations are efficient because sets are backed by hash tables.

Explanation: Sets also support symmetric_difference (^) for elements in exactly one of the two sets, which is often asked as a follow-up question.

Python
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a | b)   # union: {1, 2, 3, 4, 5, 6}
print(a & b)   # intersection: {3, 4}
print(a - b)   # difference: {1, 2}
print(a ^ b)   # symmetric difference: {1, 2, 5, 6}
35
Data StructuresMedium

How do you sort a list of dictionaries by a specific key?

Answer: Use the built-in sorted() function with the key parameter set to a lambda (or operator.itemgetter) that extracts the field you want to sort by. This returns a new sorted list without modifying the original, and you can pass reverse=True for descending order.

Explanation: sorted() uses Timsort internally, which is stable, meaning elements that compare equal retain their original relative order, and runs in O(n log n) time.

Python
students = [
    {"name": "Amit", "score": 82},
    {"name": "Priya", "score": 95},
    {"name": "Ravi", "score": 78},
]

by_score = sorted(students, key=lambda s: s["score"], reverse=True)
print([s["name"] for s in by_score])  # ['Priya', 'Amit', 'Ravi']
36
Data StructuresEasy

What is the difference between sort() and sorted()?

Answer: list.sort() is a method available only on lists; it sorts the list in place and returns None. sorted() is a built-in function that works on any iterable and returns a new sorted list, leaving the original iterable unchanged.

Explanation: A common mistake is writing my_list = my_list.sort(), which sets my_list to None because sort() does not return the sorted list.

Python
nums = [3, 1, 2]

nums.sort()             # modifies nums in place
print(nums)              # [1, 2, 3]

original = [5, 4, 6]
new_list = sorted(original)  # returns a new list
print(original, new_list)    # [5, 4, 6] [4, 5, 6]
37
Data StructuresMedium

How do you count the frequency of elements in a list?

Answer: The cleanest way is to use collections.Counter, which builds a dictionary-like object mapping each element to its count in a single call. Alternatively, you can manually build a dictionary using dict.get() or setdefault() while looping through the list.

Explanation: Counter also provides handy methods like most_common(n) to get the top n frequent elements, which is a common interview follow-up.

Python
from collections import Counter

words = ["cat", "dog", "cat", "bird", "dog", "cat"]
counts = Counter(words)
print(counts)                 # Counter({'cat': 3, 'dog': 2, 'bird': 1})
print(counts.most_common(1))  # [('cat', 3)]
38
Data StructuresMedium

What is a nested list or nested dictionary? How do you flatten a nested list?

Answer: A nested list is a list that contains other lists as elements, and a nested dictionary is a dictionary whose values are themselves dictionaries; both are used to represent hierarchical or grouped data. A simple way to flatten a one-level nested list is a list comprehension with two for clauses, iterating over each sublist and then each element within it.

Explanation: For arbitrarily deep nesting, a recursive function is typically needed instead of a single comprehension, since comprehensions only flatten one level cleanly.

Python
matrix = [[1, 2], [3, 4], [5, 6]]

flat = [item for row in matrix for item in row]
print(flat)  # [1, 2, 3, 4, 5, 6]

nested_dict = {"a": {"x": 1}, "b": {"y": 2}}
print(nested_dict["a"]["x"])  # 1
39
Data StructuresMedium

How does the "in" membership test work, and what is its time complexity for a list, set, and dictionary?

Answer: The in operator checks whether a value exists within a collection. For a list or tuple, it performs a linear scan, so it is O(n) in the worst case. For a set or a dictionary (checking keys), it uses hashing, giving average O(1) lookup time, though worst case can degrade to O(n) with many hash collisions.

Explanation: This is why sets and dictionaries are preferred over lists for frequent membership checks, especially on large datasets, such as checking if a username already exists.

Python
nums_list = [1, 2, 3, 4, 5]
nums_set = {1, 2, 3, 4, 5}

print(3 in nums_list)  # O(n) linear scan
print(3 in nums_set)   # O(1) average, hash-based lookup
40
Data StructuresMedium

What is the difference between a shallow copy and a deep copy? Give an example with a list of lists.

Answer: A shallow copy, made with copy.copy(), list(), or slicing, creates a new outer object but still references the same nested (inner) objects as the original. A deep copy, made with copy.deepcopy(), recursively copies all nested objects too, so the copy is fully independent of the original at every level.

Explanation: This distinction matters most for mutable nested structures like a list of lists: modifying a nested list through a shallow copy will also affect the original, whereas a deep copy is completely isolated.

Python
import copy

original = [[1, 2], [3, 4]]

shallow = copy.copy(original)
shallow[0].append(99)
print(original)  # [[1, 2, 99], [3, 4]] - inner list affected!

deep = copy.deepcopy(original)
deep[0].append(100)
print(original)  # unchanged by the deep copy modification

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →

Free Python Tutorial · 100 Lessons

Weak on a topic above? Revise it in minutes.

Our free, beginner-to-advanced Python tutorial covers every concept in these questions — variables, data structures, OOP, decorators, generators and more — with examples and a 30-day learning plan.

41
FunctionsEasy

What is a function in Python? What do the def keyword and return statement do, and why do we use functions?

Answer: A function is a named, reusable block of code that performs a specific task. It is defined using the def keyword, followed by a name, parentheses (with optional parameters), and a colon. The return statement sends a value back to the caller and ends the function's execution; if there is no return statement, the function returns None by default.

Explanation: Functions promote code reuse, readability, and modularity. They let us break a large program into smaller, testable pieces, avoid repeating code (DRY principle), and make debugging easier since logic is isolated in one place.

Python
def add(a, b):
    result = a + b
    return result

print(add(3, 5))   # 8

def greet(name):
    print(f"Hello, {name}!")   # no return -> returns None

output = greet("Riya")
print(output)   # None
42
FunctionsEasy

What is the difference between positional, keyword, and default arguments in Python?

Answer: Positional arguments are matched to parameters based on their order in the function call. Keyword arguments are passed as name=value pairs, so order does not matter. Default arguments have a preset value in the function definition and become optional at call time if the caller does not supply them.

Explanation: Python allows mixing all three, but positional arguments must always come before keyword arguments in a call, and parameters with default values must be defined after non-default parameters in the function signature.

Python
def student(name, age, course="Python"):
    print(name, age, course)

student("Aman", 21)                     # positional + default
student(age=22, name="Sara")            # keyword args, order doesn't matter
student("Zoya", 20, course="Data Science")  # override default
43
FunctionsMedium

What are *args and **kwargs in Python? Why are they used?

Answer: *args lets a function accept any number of extra positional arguments, collected into a tuple. **kwargs lets a function accept any number of extra keyword arguments, collected into a dictionary. They are used when the number of inputs to a function is not fixed in advance.

Explanation: The names args and kwargs are conventions, not keywords -- what matters is the single * and double ** prefixes. They are commonly used in wrapper functions, decorators, and APIs that need flexible signatures.

Python
def total(*args):
    return sum(args)

print(total(1, 2, 3, 4))   # 10

def show_details(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

show_details(name="Ravi", age=23, city="Pune")
44
FunctionsMedium

What is a lambda function in Python? When would you use one?

Answer: A lambda function is a small, anonymous, single-expression function defined using the lambda keyword instead of def. It can take any number of arguments but can only contain one expression, whose result is automatically returned.

Explanation: Lambdas are typically used for short, throwaway functions passed as arguments to higher-order functions like map(), filter(), sorted(), and reduce(), where defining a full named function would be unnecessary overhead.

Python
square = lambda x: x * x
print(square(6))   # 36

nums = [5, 2, 8, 1]
sorted_nums = sorted(nums, key=lambda x: -x)
print(sorted_nums)   # [8, 5, 2, 1]

evens = list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6]))
print(evens)   # [2, 4, 6]
45
FunctionsMedium

What is variable scope in Python? Explain local vs global scope and the global keyword.

Answer: Scope determines where a variable can be accessed. A local variable is defined inside a function and is only accessible within that function. A global variable is defined at the top level of a module and is accessible throughout the file. The global keyword, used inside a function, tells Python that an assignment should modify the global variable instead of creating a new local one.

Explanation: Without the global keyword, assigning to a name inside a function creates a new local variable, even if a global variable with the same name exists -- this is a common source of confusion and bugs for beginners.

Python
count = 0   # global variable

def increment():
    global count
    count += 1

increment()
increment()
print(count)   # 2

def show():
    count = 100   # local variable, shadows global
    print(count)

show()        # 100
print(count)  # 2 (unchanged)
46
FunctionsMedium

What is a closure in Python?

Answer: A closure is a nested (inner) function that remembers and has access to variables from its enclosing (outer) function's scope, even after the outer function has finished executing.

Explanation: Closures work because the inner function retains a reference to the outer function's local variables. They are commonly used to create function factories and to maintain state without using classes or global variables.

Python
def make_multiplier(factor):
    def multiply(number):
        return number * factor   # 'factor' is remembered
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15
47
FunctionsMedium

What is recursion? Explain with a factorial example. What is Python's recursion limit?

Answer: Recursion is when a function calls itself to solve a smaller instance of the same problem, until it reaches a base case that stops further calls. Every recursive function needs a base case (to stop) and a recursive case (that moves toward the base case). Python has a default recursion limit (usually 1000 calls deep), which can be checked with sys.getrecursionlimit() and changed with sys.setrecursionlimit(), though changing it carelessly can crash the program.

Explanation: Exceeding the recursion limit raises a RecursionError. Recursion is elegant for problems like factorial, Fibonacci, and tree traversal, but iterative solutions are often more memory-efficient in Python since it lacks tail-call optimization.

Python
def factorial(n):
    if n == 0 or n == 1:   # base case
        return 1
    return n * factorial(n - 1)   # recursive case

print(factorial(5))   # 120

import sys
print(sys.getrecursionlimit())   # 1000 (default, typically)
48
FunctionsHard

Does Python use pass by value or pass by reference for function arguments?

Answer: Python uses neither pure "pass by value" nor pure "pass by reference" -- it uses "pass by object reference" (also called "pass by assignment"). The function receives a reference to the same object the caller passed. If the object is mutable (like a list or dict) and is modified in place, the change is visible outside the function. If the object is immutable (like an int, str, or tuple), or if the parameter is reassigned to a new object, the change does not affect the caller's variable.

Explanation: This distinction trips up many candidates: reassigning a parameter inside a function never affects the caller, but mutating a mutable object through that reference does.

Python
def modify_list(lst):
    lst.append(4)   # mutates the same object

nums = [1, 2, 3]
modify_list(nums)
print(nums)   # [1, 2, 3, 4]

def modify_number(n):
    n += 10   # rebinds local name, original untouched

x = 5
modify_number(x)
print(x)   # 5
49
OOPEasy

What is OOP (Object-Oriented Programming)? What are its four main pillars?

Answer: OOP is a programming paradigm that organizes code around objects -- bundles of data (attributes) and behavior (methods) -- rather than just functions and logic. The four pillars of OOP are Encapsulation (bundling data and methods, restricting direct access), Abstraction (hiding implementation details and exposing only essentials), Inheritance (a class acquiring properties and behavior from another class), and Polymorphism (the same interface/method behaving differently for different objects).

Explanation: Python fully supports OOP through classes and objects, though it is a multi-paradigm language that also supports procedural and functional styles.

50
OOPEasy

What is a class and what is an object in Python? What does self refer to?

Answer: A class is a blueprint or template that defines the attributes and methods common to all objects of that type. An object (or instance) is a concrete realization of a class, created using the class name followed by parentheses. self refers to the specific instance calling the method -- it is how a method accesses that instance's own attributes and other methods, and it must be the first parameter of every instance method.

Explanation: self is not a keyword; it is a strong convention. Python automatically passes the instance as the first argument whenever you call a method on an object, e.g., obj.method() is really Class.method(obj).

Python
class Car:
    def __init__(self, brand):
        self.brand = brand   # instance attribute

    def show(self):
        print(f"This car is a {self.brand}")

my_car = Car("Toyota")   # object/instance
my_car.show()             # This car is a Toyota

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →
51
OOPEasy

What is __init__ in Python?

Answer: __init__ is a special method called the constructor. It is automatically called when a new object is created from a class, and it is used to initialize the object's attributes with initial values passed in as arguments.

Explanation: __init__ does not create the object itself (that is done by __new__ behind the scenes); it initializes the already-created object. It does not need to return anything (and must return None).

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

s1 = Student("Priya", 88)
print(s1.name, s1.marks)   # Priya 88
52
OOPMedium

What is the difference between instance variables and class variables?

Answer: Instance variables are defined inside methods (usually __init__) using self, and each object has its own separate copy. Class variables are defined directly inside the class body (outside any method) and are shared by all instances of that class -- changing it on the class affects every instance that hasn't overridden it.

Explanation: A common pitfall: if you assign to a class variable through an instance (self.var = ...), it creates a new instance variable that shadows the class variable for that object only, rather than modifying the shared class variable.

Python
class Employee:
    company = "TechCorp"   # class variable, shared

    def __init__(self, name):
        self.name = name    # instance variable, unique

e1 = Employee("Ali")
e2 = Employee("Neha")

print(e1.company, e2.company)   # TechCorp TechCorp

Employee.company = "InnovateX"
print(e1.company, e2.company)   # InnovateX InnovateX (shared change)
53
OOPMedium

What is inheritance in Python? What does super() do, and what types of inheritance exist?

Answer: Inheritance allows a class (child/derived class) to acquire the attributes and methods of another class (parent/base class), enabling code reuse. super() is used inside a child class to call methods (commonly __init__) of the parent class without hardcoding the parent's name. Python supports single inheritance (one parent), multiple inheritance (more than one parent), multilevel inheritance (a chain of classes), and hierarchical inheritance (multiple children from one parent).

Explanation: Multiple inheritance in Python resolves method lookup order using the MRO (Method Resolution Order / C3 linearization), viewable via ClassName.__mro__.

Python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print(f"{self.name} makes a sound")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # call parent constructor
        self.breed = breed

    def speak(self):
        print(f"{self.name} barks")

d = Dog("Tommy", "Labrador")
d.speak()   # Tommy barks
54
OOPMedium

What is polymorphism? Explain method overriding and duck typing.

Answer: Polymorphism means "many forms" -- the same method name or operator behaves differently depending on the object it is called on. Method overriding is when a child class redefines a method that already exists in its parent class, replacing the parent's behavior. Duck typing is Python's dynamic approach to polymorphism: "if it walks like a duck and quacks like a duck, it is treated as a duck" -- an object's suitability is determined by the presence of the required methods/attributes, not by its explicit type or class hierarchy.

Explanation: Because Python is dynamically typed, duck typing lets unrelated classes be used interchangeably as long as they implement the same method names, without needing a common parent class.

Python
class Cat:
    def speak(self):
        print("Meow")

class Dog:
    def speak(self):
        print("Woof")

for animal in [Cat(), Dog()]:
    animal.speak()   # Meow, Woof -- duck typing, no shared parent needed
55
OOPMedium

What is encapsulation in Python? How are public, protected, and private members implemented?

Answer: Encapsulation is the practice of bundling data and the methods that operate on it within a class, and restricting direct access to some of an object's internal details. Python has no strict access modifiers, but follows conventions: a normal attribute (name) is public. A single leading underscore (_name) signals "protected" -- intended for internal use, a convention only, not enforced. A double leading underscore (__name) triggers name mangling, making it "private" -- Python internally renames it to _ClassName__name, making accidental external access much harder.

Explanation: Name mangling is not true security; the attribute can still be accessed via its mangled name, but it strongly discourages accidental misuse from outside the class.

Python
class Account:
    def __init__(self, balance):
        self.owner = "Public"        # public
        self._pin = "Protected"      # protected (convention)
        self.__balance = balance     # private (name-mangled)

    def get_balance(self):
        return self.__balance

acc = Account(1000)
print(acc.get_balance())      # 1000
print(acc._Account__balance)  # 1000 (mangled name, still accessible)
56
OOPHard

What is abstraction in Python? How does the abc module help implement it?

Answer: Abstraction means hiding complex implementation details and exposing only the essential features/interface to the user. In Python, abstraction is implemented using Abstract Base Classes (ABCs) via the abc module. A class inherits from ABC, and methods marked with @abstractmethod must be implemented by any concrete subclass -- the abstract class itself cannot be instantiated directly.

Explanation: This enforces a contract: any subclass of the abstract class is guaranteed to implement the required methods, which is useful for designing consistent APIs and frameworks.

Python
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

c = Circle(5)
print(c.area())   # 78.5
# Shape()  # TypeError: Can't instantiate abstract class
57
OOPMedium

Does Python support method overloading? How can it be emulated?

Answer: Python does not support traditional method overloading (defining multiple methods with the same name but different parameter lists) -- if you define a method twice in a class, the later definition simply overwrites the earlier one. Instead, the same effect is emulated using default argument values, *args/**kwargs to accept a variable number of arguments, or by checking argument types/counts inside a single method.

Explanation: This is a common interview trap -- candidates who say "Python supports overloading like Java/C++" are incorrect; only the last defined method with a given name is kept.

Python
class Calculator:
    def add(self, a, b=0, c=0):
        return a + b + c

calc = Calculator()
print(calc.add(2))          # 2
print(calc.add(2, 3))       # 5
print(calc.add(2, 3, 4))    # 9
58
OOPHard

What are dunder (magic) methods in Python? Give examples like __str__, __len__, and __eq__.

Answer: Dunder (double underscore) or "magic" methods are special methods with names surrounded by double underscores that let objects integrate with Python's built-in syntax and functions. Examples: __str__ defines the human-readable string shown by print() or str(). __repr__ defines the unambiguous developer-facing representation. __len__ lets len(obj) work on custom objects. __eq__ defines behavior for the == operator between objects.

Explanation: Dunder methods are what make operator overloading and built-in function support possible for custom classes -- Python calls them implicitly rather than you calling them directly.

Python
class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

    def __str__(self):
        return f"Book: {self.title}"

    def __len__(self):
        return self.pages

    def __eq__(self, other):
        return self.title == other.title

b1 = Book("Python 101", 250)
b2 = Book("Python 101", 300)

print(b1)          # Book: Python 101
print(len(b1))     # 250
print(b1 == b2)    # True (titles match)
59
OOPMedium

What is the difference between @staticmethod, @classmethod, and a regular instance method?

Answer: An instance method takes self as its first parameter and can access/modify instance attributes and other instance methods. A @classmethod takes cls (the class itself) as its first parameter instead of self, and is used to access or modify class-level state, or as an alternative constructor -- it is called on the class or an instance. A @staticmethod takes neither self nor cls; it behaves like a plain function that is logically grouped inside the class for organizational purposes, with no access to instance or class state unless explicitly passed in.

Explanation: Use an instance method when you need object-specific data, a classmethod when you need the class itself (e.g., factory methods), and a staticmethod for utility logic that is related to the class conceptually but needs no instance or class data.

Python
class MathUtils:
    factor = 2

    def instance_method(self, x):
        return x * self.factor

    @classmethod
    def class_method(cls, x):
        return x * cls.factor

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

m = MathUtils()
print(m.instance_method(5))        # 10
print(MathUtils.class_method(5))   # 10
print(MathUtils.static_method(2, 3))  # 5
60
OOPMedium

What is the difference between __str__ and __repr__ in Python?

Answer: __str__ is meant to return a readable, user-friendly string representation of an object, used by print() and str(). __repr__ is meant to return an unambiguous, developer-oriented representation, ideally one that could be used to recreate the object; it is used by repr() and shown in the interactive shell/console when an object is displayed directly. If __str__ is not defined, Python falls back to __repr__.

Explanation: A common guideline: __repr__ should be precise and debugging-friendly (e.g., look like valid code), while __str__ should be readable for end users. Always define at least __repr__ as a good practice.

Python
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = 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)          -- uses __str__
print(repr(p))  # Point(x=2, y=3) -- uses __repr__

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →

Free Python Tutorial · 100 Lessons

Weak on a topic above? Revise it in minutes.

Our free, beginner-to-advanced Python tutorial covers every concept in these questions — variables, data structures, OOP, decorators, generators and more — with examples and a 30-day learning plan.

61
AdvancedMedium

What is a decorator in Python? Can you write a simple one?

Answer: A decorator is a function that takes another function as input, wraps it with extra behaviour, and returns a new function — without changing the original function's source code. It is applied using the @decorator_name syntax placed just above a function definition.

Explanation: Decorators are widely used for logging, timing, authentication checks, and caching. Internally, `@my_decorator` above `def func():` is just shorthand for `func = my_decorator(func)`. The inner wrapper function should use *args and **kwargs so it can decorate functions with any signature.

Python
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.time() - start:.4f}s")
        return result
    return wrapper

@timer
def slow_add(a, b):
    time.sleep(0.2)
    return a + b

slow_add(2, 3)  # prints: slow_add took 0.2001s
62
AdvancedMedium

What is a generator in Python and what does the yield keyword do?

Answer: A generator is a special kind of function that returns an iterator by using the yield keyword instead of return. Each call to next() resumes the function from where it last paused, runs until the next yield, and returns that value — the function's local state is preserved between calls.

Explanation: Generators produce values lazily, one at a time, instead of building the whole result in memory. This makes them far more memory-efficient than lists when working with large or infinite sequences, since only one item needs to exist in memory at any moment.

Python
def count_up_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

gen = count_up_to(5)
print(next(gen))   # 1
print(list(gen))   # [2, 3, 4, 5]

# List version keeps ALL items in memory:
squares_list = [x * x for x in range(1000000)]   # ~ big memory
# Generator version keeps only ONE item at a time:
squares_gen = (x * x for x in range(1000000))    # tiny memory
63
AdvancedMedium

What is an iterator in Python? How is an iterable different from an iterator?

Answer: An iterable is any object you can loop over (like a list, tuple, or string) because it implements __iter__(). An iterator is the object returned by calling iter() on an iterable — it implements both __iter__() and __next__(), and __next__() returns the next value or raises StopIteration when exhausted.

Explanation: In short: every iterator is an iterable, but not every iterable is an iterator. A list is iterable but is not itself an iterator — you cannot call next() directly on a list; you first need iter(my_list) to get an iterator object.

Python
nums = [10, 20, 30]        # iterable
it = iter(nums)             # iterator

print(next(it))  # 10
print(next(it))  # 20
print(next(it))  # 30
print(next(it))  # raises StopIteration
64
ConcurrencyMedium

What is the GIL (Global Interpreter Lock) in Python?

Answer: The GIL is a mutex inside CPython that allows only one thread to execute Python bytecode at a time, even on a multi-core machine. It exists to make CPython's memory management (like reference counting) thread-safe.

Explanation: Because of the GIL, pure Python threads cannot achieve true parallel execution for CPU-bound work — only one thread runs Python code at any instant, and threads take turns. I/O-bound code (waiting on network or disk) is unaffected because the GIL is released during I/O waits, so threading still helps there.

65
ConcurrencyMedium

What is the difference between multithreading and multiprocessing in Python? When would you use each?

Answer: Multithreading runs multiple threads inside the same process sharing memory, but the GIL means only one thread executes Python code at a time — good for I/O-bound tasks like network calls or file reads. Multiprocessing runs separate processes, each with its own Python interpreter and memory space, bypassing the GIL entirely — good for CPU-bound tasks like heavy computation.

Explanation: Rule of thumb: use the threading module (or asyncio) for I/O-bound work where the program spends time waiting; use the multiprocessing module for CPU-bound work that needs real parallel execution across cores. Multiprocessing has higher overhead (separate memory, inter-process communication) than threading.

Python
from multiprocessing import Process
from threading import Thread

def cpu_heavy():
    total = sum(i * i for i in range(10**7))

def io_heavy():
    import time
    time.sleep(1)  # simulates a network call

# CPU-bound -> multiprocessing (true parallelism)
Process(target=cpu_heavy).start()

# I/O-bound -> threading (overlaps waiting time)
Thread(target=io_heavy).start()
66
ExceptionsEasy

What is exception handling in Python? Explain try, except, else, and finally.

Answer: Exception handling lets a program deal with runtime errors gracefully instead of crashing. Code that might fail is placed in a try block; except catches and handles specific errors; else runs only if no exception occurred; finally always runs, whether an exception happened or not, and is typically used for cleanup.

Python
try:
    result = 10 / int(input("Enter a number: "))
except ValueError:
    print("That was not a valid integer")
except ZeroDivisionError:
    print("Cannot divide by zero")
else:
    print(f"Result is {result}")
finally:
    print("Execution finished")
67
ExceptionsEasy

What is the difference between an error and an exception in Python? Name a few common built-in exceptions.

Answer: An error usually refers to a serious problem, such as a SyntaxError, that a program cannot recover from and that is detected before or during execution outside normal try/except flow. An exception is a runtime event that disrupts the normal flow of a program but can be caught and handled using try/except.

Explanation: In Python, both technically derive from the BaseException class, but conventionally "errors" like SyntaxError or IndentationError stop the program before it even runs, while "exceptions" like ValueError, TypeError, KeyError, IndexError, ZeroDivisionError, FileNotFoundError, and AttributeError occur during execution and can be caught.

68
ExceptionsMedium

How do you raise an exception, and how do you create a custom exception in Python?

Answer: You raise an exception using the raise keyword, either with a built-in exception class or an instance of it. To create a custom exception, define a new class that inherits from Exception (or a more specific built-in exception).

Python
class InsufficientBalanceError(Exception):
    """Raised when a withdrawal exceeds the account balance."""
    def __init__(self, balance, amount):
        super().__init__(f"Cannot withdraw {amount}, balance is only {balance}")

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientBalanceError(balance, amount)
    return balance - amount

try:
    withdraw(100, 500)
except InsufficientBalanceError as e:
    print(e)
69
AdvancedEasy

What is the "with" statement in Python and why is it recommended for file handling?

Answer: The with statement is used to work with context managers — objects that define __enter__() and __exit__() methods. It automatically handles setup and cleanup, so a file opened with "with" is guaranteed to be closed even if an exception occurs inside the block.

Explanation: Without "with", you would need an explicit try/finally block to make sure file.close() runs even on error. "with" makes that pattern automatic and the code shorter and safer, and it is used the same way for database connections, locks, and network sockets.

Python
# Recommended
with open("data.txt", "r") as f:
    content = f.read()
# file is automatically closed here, even if read() raised an error

# Equivalent manual version
f = open("data.txt", "r")
try:
    content = f.read()
finally:
    f.close()
70
ModulesEasy

What is the difference between a module and a package in Python? How does import work?

Answer: A module is a single .py file containing Python code (functions, classes, variables) that can be imported elsewhere. A package is a directory containing multiple modules along with an __init__.py file, which groups related modules under one namespace.

Explanation: When you write "import module_name", Python searches sys.path (current directory, installed site-packages, and standard library paths) for a matching module or package, loads it once, executes its top-level code, and caches it in sys.modules so repeated imports are fast and do not re-execute the code.

Python
# Module: math_utils.py
def add(a, b):
    return a + b

# Package structure:
# mypackage/
#     __init__.py
#     math_utils.py
#     string_utils.py

from mypackage import math_utils
print(math_utils.add(2, 3))  # 5

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →
71
ModulesEasy

What is pip and what is a virtual environment? Why do we isolate project dependencies?

Answer: pip is Python's standard package manager, used to install, upgrade, and remove third-party libraries from PyPI. A virtual environment is an isolated Python environment with its own installed packages, created using venv (or tools like virtualenv/conda), separate from the system-wide Python installation.

Explanation: Different projects often need different, sometimes conflicting, versions of the same library. Virtual environments prevent "dependency hell" by keeping each project's packages isolated, making builds reproducible via a requirements.txt file, and avoiding accidental changes to the global Python installation.

Python
python -m venv myenv
source myenv/bin/activate   # on macOS/Linux
myenv\Scripts\activate      # on Windows

pip install requests
pip freeze > requirements.txt
pip install -r requirements.txt
72
MemoryMedium

What is the difference between a deep copy and a shallow copy? Why does simple assignment not create a copy at all?

Answer: Assignment (b = a) does not copy anything — it just makes a new variable name point to the same object in memory, so changes through either name affect the same data. A shallow copy (copy.copy()) creates a new outer object but still shares references to the same nested/inner objects. A deep copy (copy.deepcopy()) recursively copies every nested object, so the copy is fully independent of the original.

Python
import copy

original = [[1, 2], [3, 4]]

alias = original                  # same object, no copy
shallow = copy.copy(original)     # new outer list, same inner lists
deep = copy.deepcopy(original)    # fully independent copy

original[0][0] = 99
print(alias)    # [[99, 2], [3, 4]]  -> changed (same object)
print(shallow)  # [[99, 2], [3, 4]]  -> changed (shares inner lists)
print(deep)     # [[1, 2], [3, 4]]   -> unaffected
73
AdvancedMedium

What are decorators used for in real projects, beyond simple examples?

Answer: In real applications, decorators are commonly used for authentication/authorization checks (verifying a user is logged in before running a view), caching expensive function results (memoization), retry logic for flaky operations like network calls, input validation, and logging/monitoring — all without duplicating that logic inside every function.

Python
import time
import functools

def retry(times=3, delay=1):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except ConnectionError:
                    if attempt == times:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(times=3, delay=2)
def fetch_data():
    ...  # some network call that might fail
74
AdvancedMedium

What is monkey patching in Python?

Answer: Monkey patching means dynamically modifying or replacing a class, method, or function at runtime, outside of its original definition — instead of editing the source code directly.

Explanation: It is most often used in testing to mock out a function or method (for example, replacing a real API call with a fake one), or as a quick patch/workaround for third-party library bugs. It is powerful but risky: overusing it makes code harder to understand and debug, since behaviour differs from what is written in the original source.

Python
class Greeter:
    def hello(self):
        return "Hi"

def new_hello(self):
    return "Hello, patched!"

Greeter.hello = new_hello   # monkey patch at runtime
print(Greeter().hello())    # Hello, patched!
75
ModulesEasy

What is the purpose of if __name__ == "__main__": in a Python script?

Answer: Every Python module has a built-in variable __name__. When a file is run directly, __name__ is set to "__main__"; when the same file is imported into another module, __name__ is set to the module's filename instead. This check lets you write code that only runs when the file is executed directly, not when it is imported.

Python
# greetings.py
def greet(name):
    return f"Hello, {name}!"

if __name__ == "__main__":
    print(greet("World"))   # runs only when executed directly

# another_file.py
import greetings
greetings.greet("Alice")    # the print() above does NOT run here
76
MemoryMedium

What is garbage collection in Python? How does it manage memory?

Answer: Python primarily uses reference counting: every object tracks how many references point to it, and when that count drops to zero, the memory is freed immediately. On top of this, Python has a cyclic garbage collector (the gc module) that periodically detects and cleans up groups of objects that reference each other in a cycle but are otherwise unreachable, since reference counting alone cannot free those.

Explanation: A classic cyclic case is two objects that reference each other (a.other = b and b.other = a) but nothing outside references either one — their counts never reach zero through reference counting alone, so the cyclic collector is needed to reclaim that memory.

Python
import gc

class Node:
    def __init__(self):
        self.other = None

a = Node()
b = Node()
a.other = b
b.other = a   # reference cycle

del a, b            # counts don't hit zero due to the cycle
gc.collect()         # cyclic GC reclaims the memory
77
AdvancedMedium

Summarize the key differences between iterators and generators in Python.

Answer: A generator is a simple way to create an iterator using a function with yield (or a generator expression); it automatically implements __iter__ and __next__ for you. A plain iterator requires you to manually write a class implementing both __iter__() and __next__() and manage its internal state yourself. Generators are generally simpler to write and more memory-efficient since they compute values lazily.

Python
# Manual iterator (verbose)
class CountIterator:
    def __init__(self, n):
        self.n = n
        self.i = 1
    def __iter__(self):
        return self
    def __next__(self):
        if self.i > self.n:
            raise StopIteration
        val = self.i
        self.i += 1
        return val

# Generator (concise, same behaviour)
def count_generator(n):
    i = 1
    while i <= n:
        yield i
        i += 1
78
MemoryEasy

What is the difference between range and xrange? Does xrange still exist in Python 3?

Answer: xrange existed only in Python 2 and returned a lazy, memory-efficient sequence object rather than building a full list, while range() in Python 2 built an actual list in memory. In Python 3, xrange was removed, and range() itself now behaves like the old xrange — it returns a lightweight range object that generates numbers lazily and uses constant memory regardless of the range size.

Python
r = range(1_000_000)
print(type(r))       # <class 'range'>
print(r[500000])      # supports indexing, O(1)
print(len(r))          # supports len(), O(1)
# r itself never stores a million integers in memory
79
AdvancedMedium

What is *args unpacking and dictionary (**kwargs) unpacking when calling a function?

Answer: When calling a function, prefixing a list/tuple with * unpacks its elements as separate positional arguments, and prefixing a dictionary with ** unpacks its key-value pairs as separate keyword arguments. This is the reverse use of *args/**kwargs seen in function definitions (which collect arguments), but here it spreads existing data out into a call.

Python
def create_profile(name, age, city):
    print(f"{name}, {age}, from {city}")

info_list = ["Alice", 25, "Pune"]
create_profile(*info_list)          # unpacks list as positional args

info_dict = {"name": "Bob", "age": 30, "city": "Mumbai"}
create_profile(**info_dict)         # unpacks dict as keyword args
80
AdvancedMedium

What is the mutable default argument pitfall in Python (def f(x=[])) and how do you fix it?

Answer: Default argument values in Python are evaluated only once, when the function is defined — not each time it is called. If the default is a mutable object like a list or dict, every call that relies on the default shares and mutates the very same object, causing values to unexpectedly persist and accumulate across calls. The fix is to use None as the default and create a new mutable object inside the function body.

Python
# BUGGY
def add_item(item, bucket=[]):
    bucket.append(item)
    return bucket

print(add_item("a"))   # ['a']
print(add_item("b"))   # ['a', 'b']  <- unexpected! same list reused

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

print(add_item_fixed("a"))   # ['a']
print(add_item_fixed("b"))   # ['b']  <- correct, fresh list each time

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →

Free Python Tutorial · 100 Lessons

Weak on a topic above? Revise it in minutes.

Our free, beginner-to-advanced Python tutorial covers every concept in these questions — variables, data structures, OOP, decorators, generators and more — with examples and a 30-day learning plan.

81
CodingEasy

Write a Python program to check if a string is a palindrome.

Answer: A string is a palindrome if it reads the same forwards and backwards. The simplest fresher-friendly approach is to compare the string with its reverse using slicing `s[::-1]`.

Explanation: Slicing with a step of -1 reverses the string in O(n) time without needing a loop. For case-insensitive or space-insensitive checks, normalize with .lower() and remove spaces/punctuation first.

Python
def is_palindrome(s: str) -> bool:
    s = s.lower().replace(" ", "")
    return s == s[::-1]

print(is_palindrome("Madam"))      # True
print(is_palindrome("Hello"))      # False
print(is_palindrome("Nurses Run")) # True
82
CodingEasy

Write a Python program to find the factorial of a number, both iteratively and recursively.

Answer: Factorial n! = n * (n-1) * ... * 1, with 0! = 1. It can be computed with a simple for-loop (iterative) or a function that calls itself with a base case of 0 or 1 (recursive).

Explanation: Interviewers ask for both to check if the candidate understands recursion base cases and stack depth. Python also has math.factorial() as a built-in, worth mentioning.

Python
# Iterative
def factorial_iter(n):
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result

# Recursive
def factorial_rec(n):
    if n <= 1:
        return 1
    return n * factorial_rec(n - 1)

print(factorial_iter(5))  # 120
print(factorial_rec(5))   # 120
83
CodingEasy

Write a Python program to check if a number is prime.

Answer: A number is prime if it is greater than 1 and has no divisors other than 1 and itself. Check divisibility only up to the square root of the number for efficiency.

Explanation: A naive check loops up to n-1, but checking up to sqrt(n) is the optimized version most interviewers expect, since if n has a factor larger than its square root, it must also have one smaller.

Python
def is_prime(n: int) -> bool:
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

print(is_prime(7))   # True
print(is_prime(15))  # False
print(is_prime(1))   # False
84
CodingEasy

Write a Python program to print the Fibonacci series up to n terms.

Answer: The Fibonacci series starts with 0 and 1, and each subsequent term is the sum of the previous two. It can be implemented with a simple loop keeping two running variables, or as a generator using yield.

Explanation: A generator version is preferred in interviews for large n because it produces values lazily instead of storing the whole list in memory.

Python
def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

print(list(fibonacci(8)))
# [0, 1, 1, 2, 3, 5, 8, 13]
85
CodingEasy

Write a Python program to count the number of vowels in a string.

Answer: Loop through each character and check membership in the set of vowels "aeiou" (case-insensitive), incrementing a counter, or use a generator expression with sum().

Explanation: Using a set for vowels instead of a string/list gives O(1) membership checks instead of O(n), which is a nice efficiency point to mention.

Python
def count_vowels(s: str) -> int:
    vowels = set("aeiouAEIOU")
    return sum(1 for ch in s if ch in vowels)

print(count_vowels("Hello World"))  # 3
print(count_vowels("Python"))       # 1
86
CodingMedium

Write a Python program to find the largest and second-largest element in a list without using sort().

Answer: Traverse the list once, keeping track of the largest and second-largest values seen so far, updating them as you go. This runs in O(n) time compared to O(n log n) for sorting.

Explanation: A common mistake is using sorted(list)[-2] which works but is less efficient and is usually disallowed as it defeats the purpose of the question. Handle duplicates and edge cases (list with fewer than 2 unique elements) carefully.

Python
def largest_two(nums):
    first = second = float("-inf")
    for n in nums:
        if n > first:
            first, second = n, first
        elif first > n > second:
            second = n
    return first, second

print(largest_two([10, 5, 20, 20, 8]))  # (20, 10)
87
CodingEasy

Write a Python program to reverse a number, e.g. 123 -> 321.

Answer: Repeatedly extract the last digit using modulo 10, append it to a result, and remove that digit from the number using integer division by 10, until the number becomes 0.

Explanation: This is the arithmetic approach expected in interviews (as opposed to converting to a string and slicing, which some interviewers disallow to test loop/modulo logic). Watch out for negative numbers.

Python
def reverse_number(n: int) -> int:
    sign = -1 if n < 0 else 1
    n = abs(n)
    reversed_num = 0
    while n > 0:
        digit = n % 10
        reversed_num = reversed_num * 10 + digit
        n //= 10
    return sign * reversed_num

print(reverse_number(123))   # 321
print(reverse_number(-560))  # -65
88
CodingEasy

Write a Python program to check if two strings are anagrams of each other.

Answer: Two strings are anagrams if they contain the exact same characters with the same frequencies, regardless of order. The quickest check is to sort both strings and compare, or compare character-count dictionaries using collections.Counter.

Explanation: Counter-based comparison is O(n) and considered more efficient than sorting which is O(n log n). Remember to normalize case and strip spaces if the question intends phrase-level anagrams.

Python
from collections import Counter

def is_anagram(s1: str, s2: str) -> bool:
    s1, s2 = s1.replace(" ", "").lower(), s2.replace(" ", "").lower()
    return Counter(s1) == Counter(s2)

print(is_anagram("listen", "silent"))  # True
print(is_anagram("hello", "world"))    # False
89
CodingEasy

Write a Python program to remove duplicates from a list while preserving the original order.

Answer: Iterate through the list, keep a set of elements seen so far, and add an element to the result only if it has not been seen before. A plain set(list) removes duplicates but does not preserve order.

Explanation: From Python 3.7+, dict.fromkeys(list) is a concise one-liner that also preserves insertion order because dicts maintain insertion order, which is a nice alternative to mention.

Python
def remove_duplicates(items):
    seen = set()
    result = []
    for item in items:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

print(remove_duplicates([3, 1, 3, 2, 1, 4]))  # [3, 1, 2, 4]

# One-liner alternative (Python 3.7+, dict preserves order)
print(list(dict.fromkeys([3, 1, 3, 2, 1, 4])))  # [3, 1, 2, 4]
90
CodingEasy

Write a Python program to find duplicate elements in a list.

Answer: Use a dictionary or collections.Counter to count occurrences of each element, then collect the elements whose count is greater than 1.

Explanation: Counter.items() gives (element, count) pairs directly, avoiding manual dictionary bookkeeping. A set-based single-pass approach (tracking seen vs duplicates) is also acceptable and avoids counting all occurrences.

Python
from collections import Counter

def find_duplicates(items):
    counts = Counter(items)
    return [item for item, c in counts.items() if c > 1]

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

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →
91
CodingEasy

How do you swap two numbers without using a temporary variable in Python?

Answer: Python supports tuple unpacking, so you can swap two variables directly with `a, b = b, a`. Internally Python builds a tuple of the right-hand values first, then unpacks it into the left-hand names, so no manual temp variable is needed.

Explanation: This is Python-idiomatic; interviewers sometimes also want to hear about the arithmetic trick (a, b = a + b, a) or XOR swap for integers, but tuple unpacking is the expected and safest answer.

Python
a, b = 5, 10
a, b = b, a
print(a, b)  # 10 5
92
CodingEasy

Write a Python program to implement FizzBuzz for numbers 1 to n.

Answer: For each number from 1 to n: print "Fizz" if divisible by 3, "Buzz" if divisible by 5, "FizzBuzz" if divisible by both 3 and 5, otherwise print the number itself.

Explanation: The key gotcha is to check divisibility by 15 (or both 3 and 5) FIRST, before checking 3 or 5 individually, otherwise multiples of 15 will incorrectly print only "Fizz" or "Buzz".

Python
def fizzbuzz(n):
    for i in range(1, n + 1):
        if i % 15 == 0:
            print("FizzBuzz")
        elif i % 3 == 0:
            print("Fizz")
        elif i % 5 == 0:
            print("Buzz")
        else:
            print(i)

fizzbuzz(15)
# 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
93
Output PredictionMedium

What is the output of print([i for i in range(3)] * 2)? Explain.

Answer: The output is [0, 1, 2, 0, 1, 2]. The list comprehension [i for i in range(3)] first evaluates to [0, 1, 2], and then the `* 2` operator on a list repeats/concatenates its elements twice — it does not multiply each element by 2.

Explanation: A common fresher mistake is assuming `* 2` doubles each value inside the list (like a NumPy array would). For plain Python lists, `*` is the repetition operator, equivalent to list + list.

Python
print([i for i in range(3)] * 2)
# [0, 1, 2, 0, 1, 2]
94
Output PredictionHard

What is the output of calling def f(x=[]): x.append(1); return f() twice? Explain the mutable default argument trap.

Answer: The output is [1] on the first call and [1, 1] on the second call. Default argument values are evaluated only ONCE, when the function is defined, not each time it is called — so the same list object is reused and mutated across calls.

Explanation: This is one of the most common Python gotcha interview questions. The fix is to use `def f(x=None): if x is None: x = []` so a fresh list is created on every call instead of sharing one mutable default across all invocations.

Python
def f(x=[]):
    x.append(1)
    return x

print(f())  # [1]
print(f())  # [1, 1]  <- unexpected! same list object reused

# Correct fix:
def f_fixed(x=None):
    if x is None:
        x = []
    x.append(1)
    return x

print(f_fixed())  # [1]
print(f_fixed())  # [1]  <- fresh list each call
95
Output PredictionHard

What is the output of comparing `a is b` for small integers versus large integers in Python? Explain integer caching.

Answer: For small integers in the range -5 to 256, CPython pre-caches (interns) integer objects, so two variables holding the same small value point to the same object and `a is b` returns True. For integers outside this range, each literal typically creates a new object, so `a is b` returns False even though `a == b` is True.

Explanation: This is CPython implementation detail, not a language guarantee — it should never be relied on in real code. `is` checks object identity (same memory location), while `==` checks value equality; use `==` for comparing values.

Python
a = 100
b = 100
print(a is b)  # True (small int, cached, range -5 to 256)

c = 1000
d = 1000
print(c is d)  # False (outside cached range, different objects)
print(c == d)  # True  (values are equal)
96
LibrariesMedium

What is NumPy and why would you use it instead of plain Python lists?

Answer: NumPy is a library for fast numerical computing in Python, built around the ndarray — a fixed-type, contiguous array. It is much faster than plain lists for numeric operations because it uses vectorized C-level loops instead of Python-level for-loops, and it supports element-wise math directly.

Explanation: With a plain list, `[1, 2, 3] * 2` repeats the list; with a NumPy array, the same operation multiplies every element by 2 (vectorization). NumPy arrays also use much less memory than lists of Python int/float objects.

Python
import numpy as np

arr = np.array([1, 2, 3])
print(arr * 2)          # [2 4 6]  (element-wise)

lst = [1, 2, 3]
print(lst * 2)           # [1, 2, 3, 1, 2, 3]  (repetition)
97
LibrariesMedium

What is Pandas and what is a DataFrame?

Answer: Pandas is a Python library for data manipulation and analysis, built on top of NumPy. A DataFrame is its core 2D, table-like data structure with labeled rows and columns, similar to a spreadsheet or SQL table, and it supports filtering, grouping, merging, and reading/writing files like CSV or Excel.

Explanation: Pandas also has a 1D structure called a Series (essentially a single column of a DataFrame). Freshers should be able to show a minimal example of creating a DataFrame and selecting a column.

Python
import pandas as pd

data = {"name": ["Amit", "Riya"], "age": [22, 24]}
df = pd.DataFrame(data)

print(df)
#     name  age
# 0   Amit   22
# 1   Riya   24

print(df["age"].mean())  # 23.0
98
LibrariesMedium

How do you make an HTTP GET request in Python and read the response?

Answer: The `requests` library is the standard, most widely used way to make HTTP calls in Python. `requests.get(url)` sends a GET request and returns a Response object with attributes like `.status_code`, `.text`, and `.json()`.

Explanation: For JSON APIs, `.json()` parses the response body directly into a Python dict/list. Always check `response.status_code` (or use `response.raise_for_status()`) before trusting the response body.

Python
import requests

response = requests.get("https://api.example.com/data")
print(response.status_code)  # 200
data = response.json()       # parsed dict/list
print(data)
99
LibrariesEasy

How do you read and parse a JSON file in Python?

Answer: Use the built-in `json` module. Open the file and pass the file object to `json.load()` to parse it directly into a Python dict/list, or use `json.loads()` if you already have a JSON string in memory.

Explanation: The counterpart functions are `json.dump()` (write a Python object to a file as JSON) and `json.dumps()` (convert a Python object to a JSON string). Always open files with a `with` block so they are closed automatically.

Python
import json

# Reading a JSON file
with open("data.json") as f:
    data = json.load(f)
print(data)

# Parsing a JSON string
json_str = '{"name": "Amit", "age": 22}'
parsed = json.loads(json_str)
print(parsed["name"])  # Amit
100
PracticalMedium

How do you handle a very large file efficiently in Python without running out of memory?

Answer: Avoid reading the whole file into memory with `.read()` or `.readlines()`. Instead, iterate over the file object directly (or use a loop with `.readline()`), which reads and processes one line at a time, keeping memory usage constant regardless of file size.

Explanation: File objects in Python are iterators/generators under the hood, so `for line in f:` is both memory-efficient and idiomatic. For structured chunked processing (not line-based), read fixed-size byte chunks with `f.read(chunk_size)` in a loop instead.

Python
def process_large_file(path):
    with open(path) as f:
        for line in f:          # reads one line at a time, lazily
            line = line.strip()
            if line:
                process(line)

def process(line):
    print(line)

Looking for Python Developer Jobs?

Apply to fresher Python developer & backend roles hiring across India right now.

View Python Developer Jobs →

Free Python Tutorial · 100 Lessons

Weak on a topic above? Revise it in minutes.

Our free, beginner-to-advanced Python tutorial covers every concept in these questions — variables, data structures, OOP, decorators, generators and more — with examples and a 30-day learning plan.

Ready to land your first Python role?

Revise with the free tutorial, then apply to Python developer jobs and internships across India.