20 Python Interview Questions That Actually Get Asked in 2026 (With Straight Answers)
Most Python interview question lists online are long collections of syntax trivia. This guide takes a more practical approach: 20 questions covering the Python concepts that candidates are commonly expected to understand in junior-to-mid-level interviews.
The questions cover core language fundamentals, data structures, functions, object-oriented programming, memory and scope, the Global Interpreter Lock (GIL), and exception handling. More importantly, the answers explain why each concept works the way it does.
Table of Contents
- Introduction
- Core Language Fundamentals
- Data Structures Questions
- Functions and Functional Concepts
- Object-Oriented Python
- Memory, Scope, and the GIL
- Exception Handling
- How These Questions Actually Get Used
- Learning Python at Innovative Academy
- FAQs
- Final Thoughts
1. Introduction
Python interviews are rarely about memorizing hundreds of isolated syntax rules. Interviewers often use a smaller set of concepts to determine whether a candidate has actually written and debugged Python code.
Questions about mutable default arguments, generators, shallow versus deep copies, decorators, object-oriented programming, exception handling, and the GIL can reveal whether a candidate understands how Python behaves beyond basic syntax.
The following 20 questions are organized by topic so you can use them as an interview preparation checklist as well as a practical Python revision guide.
2. Core Language Fundamentals
Q1. What is the difference between a list and a tuple?
A list is mutable, which means its contents can be changed after creation. You can append, remove, replace, or otherwise modify elements in a list.
A tuple is immutable. Once created, its elements cannot be changed.
For example:
my_list = [10, 20, 30]
my_list.append(40)
my_tuple = (10, 20, 30)
Tuples can also be used as dictionary keys when all their elements are hashable. Lists cannot be dictionary keys because they are mutable.
In practice, use a list when the collection needs to change and a tuple when you want an immutable sequence.
Q2. What does is check that == doesn't?
== compares values. It asks whether two objects contain equivalent data.
is compares object identity. It asks whether two references point to the exact same object.
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True
print(a is b) # False
The two lists contain the same values, but they are separate objects.
This distinction is particularly important when working with values such as small integers or strings that Python may internally cache or reuse. For value comparison, use ==. For identity checks, use is.
Q3. How does Python handle mutable default arguments, and why is it a trap?
Consider this function:
def add_item(item, basket=[]):
basket.append(item)
return basket
It may look as though a new empty list is created every time the function is called without a basket argument. That is not what happens.
Python evaluates the default argument once, when the function is defined. The same list object is then reused across calls.
print(add_item("apple"))
print(add_item("banana"))
The second call can contain the value from the first call because both calls use the same default list.
The standard solution is to use None as the default:
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
This creates a new list whenever the function is called without an explicit basket.
Q4. What is the difference between shallow copy and deep copy?
A shallow copy creates a new outer container but keeps references to the same nested objects.
A deep copy recursively creates copies of nested objects as well.
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
If a nested list is changed through shallow, the corresponding nested object in original can also change because both contain references to the same nested object.
With deepcopy(), nested objects are copied recursively, so the resulting structure is independent of the original.
3. Data Structures Questions
Q5. When would you use a set instead of a list?
A set is useful when you need unique values or frequent membership testing.
For example:
skills = {"Python", "AWS", "Linux"}
if "Python" in skills:
print("Python found")
Set membership is typically very fast because sets use a hash-table-based implementation. Sets also automatically eliminate duplicate values.
Use a list when ordering and duplicate values matter. Use a set when uniqueness and membership testing are the main requirements.
Q6. What's the difference between a dictionary and a list of tuples?
A dictionary maps keys to values and is designed for lookup by key.
student = {
"name": "Rahul",
"course": "Python",
"experience": 1
}
A list of tuples stores individual pairs while preserving their sequence and allowing duplicate keys:
students = [
("name", "Rahul"),
("course", "Python")
]
If you need to repeatedly retrieve information using a unique identifier, a dictionary is generally more appropriate. A list of tuples can be useful when ordering or duplicate entries are important.
Regular dictionaries preserve insertion order in modern Python versions, so older patterns that relied on collections.OrderedDict solely for insertion ordering are often unnecessary.
Q7. How do list comprehensions compare to a for loop with .append()?
A list comprehension and a conventional loop can produce the same result.
numbers = [1, 2, 3, 4]
doubled = [x * 2 for x in numbers]
The equivalent loop is:
doubled = []
for x in numbers:
doubled.append(x * 2)
List comprehensions are often concise and can be faster for simple transformations because Python implements the comprehension efficiently.
However, readability should come first. A complicated comprehension containing multiple conditions or deeply nested expressions can be harder to understand than a normal loop.
4. Functions and Functional Concepts
Q8. What are *args and **kwargs?
*args collects additional positional arguments into a tuple.
**kwargs collects additional keyword arguments into a dictionary.
def show_details(*args, **kwargs):
print(args)
print(kwargs)
show_details("Python", "AWS", level="beginner", mode="online")
These features are especially useful when creating flexible functions, wrappers, and decorators that need to accept and forward an unknown number of arguments.
Q9. What is a decorator, and what problem does it solve?
A decorator is a function that takes another function and returns a modified or wrapped version of that function.
Decorators are commonly used for logging, timing, authentication, access control, and other reusable behaviors.
def log_call(func):
def wrapper():
print("Function called")
return func()
return wrapper
@log_call
def greet():
print("Hello")
The @log_call syntax applies the decorator to the function.
The main benefit is that additional behavior can be reused without repeatedly changing the original function's implementation.
Q10. What's the difference between a generator and a regular function that returns a list?
A regular function that returns a list generally creates the complete result and stores it in memory.
A generator uses yield to produce values one at a time.
def numbers():
for i in range(5):
yield i
When a value is requested from the generator, execution resumes from where it previously stopped.
This approach can be useful when working with large datasets or streams of data because the complete result does not need to be stored in memory at once.
Q11. What does a lambda function do that a regular function doesn't?
A lambda does not provide a capability that a regular function cannot provide. It is simply a compact way of creating an anonymous function containing a single expression.
numbers = [5, 2, 9, 1]
numbers.sort(key=lambda x: x)
Lambda functions are frequently used with functions such as sorted(), map(), and filter().
Because a lambda is restricted to a single expression, a normal def function is generally more appropriate when the logic becomes complex.
5. Object-Oriented Python
Q12. What's the difference between a class method, a static method, and an instance method?
An instance method receives self and works with a particular object instance.
A class method receives cls and works with the class itself. It is created using @classmethod.
A static method receives neither self nor cls. It is placed inside a class for organizational purposes and is created using @staticmethod.
class Employee:
def instance_method(self):
return "Instance method"
@classmethod
def class_method(cls):
return "Class method"
@staticmethod
def static_method():
return "Static method"
Q13. What is method resolution order (MRO), and when does it matter?
Method Resolution Order, or MRO, is the sequence Python follows when searching for a method or attribute through a class's inheritance hierarchy.
It becomes especially important with multiple inheritance.
class A:
def show(self):
print("A")
class B(A):
pass
print(B.mro())
Python uses C3 linearization to calculate a consistent MRO.
If multiple parent classes provide methods with the same name, the MRO determines which implementation Python finds first.
Q14. What's the difference between __init__ and __new__?
__new__ is responsible for creating and returning a new instance. It runs before __init__.
__init__ initializes an instance that has already been created.
class Person:
def __new__(cls):
print("Creating object")
return super().__new__(cls)
def __init__(self):
print("Initializing object")
Most everyday Python classes only need __init__. Overriding __new__ is more common in specialized situations involving immutable types, metaprogramming, or particular object-creation patterns.
6. Memory, Scope, and the GIL
Q15. What is the Global Interpreter Lock (GIL), and why does it matter for multithreading?
The Global Interpreter Lock, commonly called the GIL, is a mechanism in the traditional CPython execution model that restricts simultaneous execution of Python bytecode by multiple threads within a process.
This means Python threads do not generally provide CPU-bound Python code with straightforward parallel execution across multiple CPU cores in the traditional CPython model.
Threads can still be very useful for I/O-bound workloads such as network requests and file operations because threads can make progress while another thread is waiting for I/O.
For CPU-intensive workloads, developers may consider multiprocessing or other approaches depending on the application and Python runtime being used.
Q16. What's the difference between local, global, and nonlocal scope?
A variable created inside a function is normally local to that function.
The global keyword allows a function to assign to a variable defined at module level.
The nonlocal keyword allows a nested function to modify a variable belonging to an enclosing function.
x = 10
def outer():
y = 20
def inner():
nonlocal y
y += 1
inner()
return y
Understanding these scopes helps prevent unexpected behavior when functions and nested functions share variables.
7. Exception Handling
Q17. What's the difference between an exception and a syntax error?
A syntax error means Python cannot parse the source code as valid Python.
if True
print("Hello")
The missing colon causes a syntax error.
An exception occurs while syntactically valid code is running.
number = 10 / 0
This produces a ZeroDivisionError.
Runtime exceptions can often be handled with try and except, while syntax errors need to be corrected in the source code.
Q18. What does the finally block guarantee?
The code inside a finally block is designed to execute whether the try block completes normally or an exception occurs.
try:
file = open("data.txt")
data = file.read()
except FileNotFoundError:
print("File not found")
finally:
print("Cleanup section")
This makes finally useful for cleanup operations such as releasing resources or closing handles.
There are exceptional control-flow situations where normal execution can be interrupted, so finally should not be interpreted as an absolute guarantee against every possible termination scenario.
Q19. How do you create and raise a custom exception?
You can create a custom exception by defining a class that inherits from Exception or another suitable exception class.
class InsufficientFundsError(Exception):
pass
raise InsufficientFundsError("Insufficient balance")
Custom exceptions make error handling more precise because code higher in the call stack can catch a specific type of error rather than inspecting generic exception messages.
Q20. What happens if an exception is raised inside an except block itself?
If another exception occurs while an exception is being handled, the new exception propagates upward.
Python also preserves information about the original exception through exception chaining.
try:
1 / 0
except ZeroDivisionError:
raise ValueError("A new error occurred")
The traceback can show both exceptions and indicate that the second exception occurred while handling the first.
This behavior is important when debugging because the original problem may provide useful context for understanding why the later exception occurred.
8. How These Questions Actually Get Used
These questions are deliberately practical. Interviewers are often less interested in whether a candidate can recite a definition and more interested in whether the candidate can apply the concept to a real programming situation.
For example, an interviewer might ask why a function unexpectedly retains values between calls, why a generator is preferable to building a huge list, or why adding threads did not improve the performance of CPU-heavy Python code.
The strongest preparation therefore goes beyond memorizing answers. Candidates should write small examples, intentionally create the relevant behavior, observe the result, and then explain why Python behaved that way.
9. Learning Python at Innovative Academy
Building interview confidence requires more than reading Python concepts. Regular coding practice helps learners understand how Python behaves when they actually create, modify, debug, and run programs.
Innovative Academy's Python Training in Bangalore focuses on practical Python learning and hands-on coding practice for learners preparing to build programming skills and pursue IT opportunities.
For students preparing for Python interviews, practicing concepts such as data structures, functions, decorators, generators, object-oriented programming, and exception handling can help turn theoretical knowledge into practical understanding.
10. Frequently Asked Questions
1. Are these the exact questions I'll get asked in a real interview?
Not necessarily. Interviewers can phrase questions differently and may adapt them to the role and candidate's experience. However, concepts such as mutable default arguments, generators, shallow versus deep copying, exception handling, OOP, and Python concurrency are useful areas to prepare.
2. Do I need to memorize these answers exactly as written?
No. Understanding the underlying concepts is more useful than memorizing sentences.
A good preparation method is to run each example yourself. For instance, create the mutable-default-argument problem, observe the output, fix it using None, and then explain why the original behavior occurred.
3. Which topics can appear in a practical coding round?
Topics such as decorators, generators, functions, data structures, exception handling, and object-oriented programming can all appear in practical exercises. The exact tasks depend on the company and role.
4. Is the GIL still relevant when Python applications use asynchronous programming?
Yes. Understanding the GIL remains useful when deciding between threading, multiprocessing, asynchronous programming, and other concurrency approaches.
Asynchronous programming is particularly useful for managing I/O-bound operations efficiently, but it does not simply remove all considerations related to CPU-bound execution.
5. Should a complete beginner learn all 20 questions before a first interview?
A beginner can start with the fundamentals: variables, data types, lists, tuples, dictionaries, sets, functions, loops, and basic exception handling.
Once those concepts are comfortable, topics such as decorators, generators, OOP, scope, memory behavior, and concurrency can be introduced progressively.
11. Final Thoughts
The difference between someone who has merely read about Python and someone who has actually written Python often becomes visible when they encounter practical problems.
Understanding why mutable default arguments behave unexpectedly, when to use a generator, how object identity differs from equality, or why threading may not accelerate CPU-bound Python code demonstrates a deeper level of Python knowledge.
Use these 20 questions as a practice checklist rather than a script to memorize. Write the examples yourself, experiment with the behavior, and practice explaining each concept in your own words. That approach can make Python interview preparation more practical and effective.