Python is asked in interviews for backend, data science, automation, testing and freshers' roles. These Python interview questions cover data types, lists, dictionaries and functions for beginners, then OOP, decorators, generators, the GIL, memory management, concurrency and best practices for experienced Python developers.
40 questions · Updated
Reading answers is not the same as saying them.Practise Python questions out loud and get a score, what you missed and a model answer for each one.
Practise free with AIPython Interview Questions: 40 questions with model answers on this page.
Basic Python interview questions
Fundamentals, definitions and simple scenarios. Good for freshers and warm-ups.
1. What are the main features of Python?
Python is an interpreted, dynamically typed, high-level language with simple, readable syntax that uses indentation for blocks. It supports procedural, object-oriented and functional styles, manages memory automatically and has a huge standard library and ecosystem for web development, data science, AI, automation and scripting.
2. What is the difference between a list, a tuple, a set and a dictionary?
Answer flow · 4 steps
1
A list is ordered and mutable and allows duplicates, such as [1, 2, 2].
2
A tuple is ordered and immutable, so it can be used as a dictionary key, such as (1, 2).
3
A set is unordered with unique elements and fast membership tests.
4
A dictionary stores key-value pairs with fast lookup by key and keeps insertion order since Python 3.7.
3. What is the difference between mutable and immutable objects?
Answer flow · 3 steps
1
Mutable objects such as lists, dictionaries and sets can be changed in place.
2
Immutable objects such as int, float, str, tuple and frozenset cannot; any "change" creates a new object.
3
This matters when passing objects to functions, because a function can modify a mutable argument, and it is why a mutable default argument like def f(items=[]) is a common bug.
4. What is the difference between is and ==?
Answer flow · 3 steps
1
== checks whether two objects have equal values, using the __eq__ method. is checks whether two names point to the exact same object in memory.
2
Two separate lists [1, 2] == [1, 2] is True, but [1, 2] is [1, 2] is False.
3
Use is only for singletons such as None (if x is None), and == for comparing values.
5. How do you reverse a string or a list in Python?
Slicing with a step of -1 works for both: text[::-1] returns a reversed copy of a string, and items[::-1] a reversed copy of a list. items.reverse() reverses a list in place and returns None, and reversed(items) returns an iterator, so "".join(reversed(text)) also reverses a string. Strings are immutable, so they cannot be reversed in place.
6. What is PEP 8?
Answer flow · 4 steps
1
PEP 8 is Python's official style guide.
2
It covers four-space indentation, line length, naming (snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants), spacing, import order and more.
3
Following it makes code consistent and easy for others to read.
4
Tools such as Ruff, Black and flake8 check and format code automatically.
7. What do break, continue and pass do?
break exits the nearest loop immediately. continue skips the rest of the current iteration and moves to the next one. pass does nothing; it is a placeholder where Python requires a statement, such as an empty function or class you will fill in later. Loops can also have an else block that runs only if the loop finished without hitting break.
8. What is a lambda function?
Answer flow · 3 steps
1
A lambda is a small anonymous function written in one expression, such as lambda x: x * 2.
2
It is mostly used as a short argument to functions like sorted, map and filter, for example sorted(users, key=lambda u: u["age"]).
3
For anything longer than one simple expression, a normal def function with a name is clearer and easier to test.
9. Explain local, global and nonlocal variables.
Answer flow · 5 steps
1
A variable assigned inside a function is local to it.
2
A variable assigned at the top of a module is global.
3
Python looks up names in the order Local, Enclosing, Global, Built-in (the LEGB rule).
4
To assign to a global variable inside a function you declare it with global, and to assign to a variable in an enclosing function from a nested function you use nonlocal.
5
Relying on globals makes code harder to test.
10. What are modules and packages?
Answer flow · 4 steps
1
A module is a single .py file that you can import.
2
A package is a directory of modules, traditionally with an __init__.py file, that groups related code, imported with dotted names like package.module.
3
Third-party packages are installed from PyPI with pip.
4
Organising code into modules and packages keeps projects manageable and avoids name clashes.
11. How does exception handling work in Python?
Code that may fail goes in a try block. except blocks catch specific exceptions, such as ValueError or KeyError; else runs only if no exception occurred; and finally always runs, which suits cleanup. raise throws an exception, and custom exceptions subclass Exception. I catch the narrowest exception possible and avoid a bare except, which hides real bugs.
try:
age = int(user_input)
except ValueError:
print("Please enter a number")
else:
print("Age saved:", age)
finally:
print("Done")
12. What are f-strings?
Answer flow · 3 steps
1
f-strings, added in Python 3.6, let you put expressions directly inside string literals by prefixing the string with f: f"Hello {name}, you have {count} messages".
2
They support formatting such as f"{price:.2f}" for two decimal places and f"{value=}" for debugging, which prints both the expression and its value.
3
They are faster and more readable than % formatting or str.format.
13. How do you read and write files in Python?
Answer flow · 4 steps
1
Use open() inside a with statement, which closes the file automatically even if an error occurs: with open("data.txt", encoding="utf-8") as f: text = f.read().
2
Mode "r" reads, "w" overwrites, "a" appends, and "b" is for binary files.
3
Iterating over the file object reads it line by line, which is memory-efficient for large files.
4
The csv, json and pathlib modules help with common formats and paths.
Intermediate Python interview questions
Applied problems, trade-offs and questions about your own projects.
14. What are list comprehensions and generator expressions?
Answer flow · 3 steps
1
A list comprehension builds a list in one line, such as [x * x for x in range(10) if x % 2 == 0].
2
A generator expression uses parentheses, such as (x * x for x in range(10)), and produces values lazily one at a time, using very little memory, so it is better for large data or when you only iterate once.
3
Dictionary and set comprehensions also exist.
15. What are *args and **kwargs?
Answer flow · 3 steps
1
*args collects extra positional arguments into a tuple, and **kwargs collects extra keyword arguments into a dictionary, so a function can accept a variable number of arguments.
2
They are often used in wrappers and decorators to pass arguments through, such as func(*args, **kwargs).
3
The same symbols unpack lists and dictionaries when calling a function.
16. What is a decorator?
Answer flow · 3 steps
1
A decorator is a function that takes another function and returns a new function that adds behaviour before or after it, without changing its code.
2
It is applied with @decorator above a function.
3
Examples include logging, timing, caching (functools.lru_cache), authentication checks and @app.route in Flask. functools.wraps keeps the original function's name and docstring.
17. What are generators, and what does yield do?
Answer flow · 3 steps
1
A generator is a function that uses yield to produce a series of values lazily.
2
Each call to next() runs the function until the next yield, returns that value and pauses, keeping its local state.
3
Generators save memory when processing large files or infinite sequences, because values are produced only when needed.
18. What is the difference between a shallow copy and a deep copy?
Answer flow · 3 steps
1
A shallow copy, made with copy.copy(), list(x) or x[:], creates a new outer object but keeps references to the same inner objects, so changing a nested list affects both copies.
2
A deep copy, made with copy.deepcopy(), recursively copies every nested object, so the two are completely independent.
3
Deep copies are slower and use more memory, so use them only when needed.
19. What are __init__ and self?
__init__ is the initialiser method that runs when you create an object, used to set its starting attributes. self is the conventional name for the first parameter of instance methods; it refers to the object the method is called on, so self.name = name stores data on that particular instance. Python passes self automatically when you call obj.method().
20. What is the difference between instance methods, class methods and static methods?
Answer flow · 3 steps
1
An instance method receives self and works with one object's data.
2
A class method, marked with @classmethod, receives the class as cls and is often used as an alternative constructor, such as User.from_dict(data).
3
A static method, marked with @staticmethod, receives neither and is just a function grouped with the class because it is related to it.
21. How does inheritance work in Python, and what is the MRO?
Answer flow · 3 steps
1
A class inherits from one or more parent classes, reusing and overriding their methods. super() calls the next method in the method resolution order.
2
With multiple inheritance, Python uses the C3 linearisation algorithm to decide the MRO, the order in which classes are searched for a method, which you can see with ClassName.__mro__.
3
Mixins are a common use of multiple inheritance.
22. What is the difference between __str__ and __repr__?
__str__ returns a readable, user-friendly description used by print() and str(). __repr__ returns an unambiguous developer-facing representation, ideally one that could recreate the object, used in the shell, in debugging and inside containers. If __str__ is not defined, Python falls back to __repr__, so defining __repr__ is the priority.
23. What is the difference between an iterable and an iterator?
Answer flow · 4 steps
1
An iterable is any object you can loop over, such as a list, string or dictionary; it has an __iter__ method that returns an iterator.
2
An iterator produces values one at a time with __next__ and raises StopIteration when finished; it can be used only once.
3
A for loop calls iter() on the iterable and then next() repeatedly.
4
Generators are a simple way to create iterators.
24. What is a context manager?
Answer flow · 3 steps
1
A context manager sets something up and guarantees cleanup, used with the with statement.
2
Files, locks and database connections are common examples.
3
You create one with a class that defines __enter__ and __exit__, or more simply with the @contextmanager decorator from contextlib and a generator that yields once.
from contextlib import contextmanager
import time
@contextmanager
def timer(label):
start = time.perf_counter()
yield
print(label, round(time.perf_counter() - start, 3), "s")
with timer("load"):
load_data()
25. What is a virtual environment, and why use one?
Answer flow · 3 steps
1
A virtual environment is an isolated Python installation for one project, with its own installed packages, created with python -m venv .venv.
2
It prevents version conflicts between projects and keeps the system Python clean.
3
Dependencies are recorded in requirements.txt or pyproject.toml with a lock file, and tools such as uv, Poetry or pip-tools make this faster and reproducible.
26. How do map, filter, zip and enumerate work?
Answer flow · 3 steps
1
map(func, items) applies a function to every item, filter(func, items) keeps items where the function returns True, zip(a, b) pairs up items from several iterables, and enumerate(items) gives (index, item) pairs, which is cleaner than range(len(items)).
2
All return lazy iterators.
3
List comprehensions are often more readable than map and filter.
27. Which collections module classes do you use?
Counter counts items, such as Counter(words).most_common(3). defaultdict creates missing keys with a default, such as defaultdict(list) for grouping. deque is a double-ended queue with fast appends and pops at both ends, useful for queues and sliding windows. namedtuple creates lightweight tuples with named fields, and OrderedDict has extra methods such as move_to_end.
28. How do you sort complex data in Python?
Answer flow · 4 steps
1
sorted() returns a new sorted list and list.sort() sorts in place.
2
Both accept key, a function that returns the value to sort by, and reverse.
3
For example, sorted(employees, key=lambda e: (e.dept, -e.salary)) sorts by department and then salary in descending order. operator.itemgetter and attrgetter are fast alternatives to lambdas.
4
Python's sort, Timsort, is stable.
29. What does if __name__ == "__main__" mean?
Answer flow · 3 steps
1
Every module has a __name__ variable.
2
When a file is run directly, its __name__ is "__main__"; when it is imported, __name__ is the module's name.
3
So code inside this if block, such as starting the program or running a demo, only runs when the file is executed directly, not when another module imports its functions.
High level Python interview questions
System design, deep internals, leadership and tough follow-ups.
30. What is the GIL, and how does it affect concurrency?
Answer flow · 4 steps
1
The Global Interpreter Lock in CPython allows only one thread to run Python bytecode at a time.
2
Threads still help for I/O-bound work, such as network calls, because the GIL is released while waiting.
3
For CPU-bound work I use multiprocessing or concurrent.futures.ProcessPoolExecutor, or libraries like NumPy that run in C. asyncio handles many concurrent I/O tasks in one thread.
4
Python 3.13 added an experimental free-threaded build without the GIL.
31. How does memory management work in Python?
Answer flow · 4 steps
1
CPython uses reference counting: each object tracks how many references point to it and is freed when the count reaches zero.
2
A cyclic garbage collector finds and frees groups of objects that reference each other.
3
Small objects use a private memory allocator.
4
Memory leaks usually come from lingering references in global caches or long-lived objects, and tracemalloc helps find them.
32. What is the difference between multithreading, multiprocessing and asyncio?
Multithreading runs threads in one process sharing memory; because of the GIL it suits I/O-bound tasks. Multiprocessing runs separate processes with their own interpreter and memory, which gives true parallelism for CPU-bound tasks at the cost of more memory and inter-process communication. asyncio uses a single-threaded event loop with async and await to handle thousands of concurrent I/O operations efficiently, such as web requests.
33. What are type hints, and do they affect runtime?
Answer flow · 3 steps
1
Type hints annotate the expected types of variables, parameters and return values, such as def total(prices: list[float]) -> float.
2
Python does not enforce them at runtime; they are used by static checkers such as mypy and Pyright, by editors for autocomplete and error detection, and by libraries such as Pydantic and FastAPI, which read them to validate data.
3
They make large codebases much safer to change.
34. What are dataclasses?
Answer flow · 3 steps
1
The @dataclass decorator automatically generates __init__, __repr__ and __eq__ from class attributes with type hints, removing boilerplate for classes that mainly hold data.
2
Options include frozen=True for immutable objects, order=True for comparisons, slots=True for lower memory use, and field() for defaults like empty lists.
3
For validation of external data, Pydantic models are often used instead.
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Order:
id: int
customer: str
items: list[str] = field(default_factory=list)
35. What does the @property decorator do?
Answer flow · 4 steps
1
@property turns a method into an attribute that is accessed without parentheses, so you can compute values or add validation while keeping a simple interface.
2
A matching @name.setter runs logic when the attribute is assigned, for example rejecting a negative price.
3
This lets you start with plain attributes and add logic later without changing code that uses the class.
4
Properties are built on descriptors.
36. What is a metaclass?
Answer flow · 3 steps
1
A metaclass is the class of a class: it controls how classes themselves are created. type is the default metaclass.
2
A custom metaclass can modify or register classes when they are defined, which frameworks such as Django's ORM have used to build models.
3
They are rarely needed in application code; class decorators and __init_subclass__ are simpler ways to achieve most of the same goals.
37. How do you run many network requests concurrently with asyncio?
Answer flow · 3 steps
1
Define async functions that await I/O and run them together with asyncio.gather or a TaskGroup, using an async HTTP client such as httpx or aiohttp.
2
A semaphore limits how many run at once so you do not overload the server.
3
Blocking calls such as requests or time.sleep must not be used inside async code because they block the whole event loop.
import asyncio, httpx
async def fetch_all(urls):
limit = asyncio.Semaphore(10)
async with httpx.AsyncClient() as client:
async def fetch(url):
async with limit:
r = await client.get(url)
return r.status_code
return await asyncio.gather(*(fetch(u) for u in urls))
results = asyncio.run(fetch_all(urls))
38. How do you make slow Python code faster?
Answer flow · 3 steps
1
First I measure with cProfile or py-spy to find the real hot spots.
2
Then I choose better algorithms and data structures, such as a set or dict for lookups instead of a list, use built-in functions and comprehensions, avoid repeated work with caching (functools.lru_cache), and use NumPy or pandas vectorised operations instead of Python loops for numeric data.
3
For CPU-bound work I use multiprocessing, and for I/O-bound work asyncio or threads.
39. How do you test Python code with pytest?
Answer flow · 3 steps
1
pytest finds functions named test_* and uses plain assert statements with detailed failure messages.
2
Fixtures provide reusable setup such as test data or a database connection, @pytest.mark.parametrize runs one test with many inputs, and unittest.mock or pytest-mock replace external services. pytest-cov measures coverage.
3
Tests run in CI on every change.
40. How do you package and distribute a Python project?
Answer flow · 4 steps
1
Modern projects describe their metadata, dependencies and build system in pyproject.toml.
2
A build backend such as Hatchling or setuptools builds a wheel and a source distribution with python -m build, and twine or uv publishes them to PyPI.
3
Version numbers follow semantic versioning, and entry points can create command-line tools.
4
Applications are often shipped as Docker images instead.
Python machine coding round questions
Tasks you build live in 30 to 60 minutes. What interviewers look for, plus a compact solution to compare with yours.
Check whether a string is a palindrome, ignoring case and punctuation.
Answer flow · 3 steps
1
A common fresher program.
2
Clean the string first, then compare it with its reverse.
3
Mention the two-pointer approach, which uses O(1) extra space.
def is_palindrome(text):
cleaned = [c.lower() for c in text if c.isalnum()]
return cleaned == cleaned[::-1]
is_palindrome("A man, a plan, a canal: Panama") # True
Find the most frequent words in a text.
Answer flow · 3 steps
1
Tests strings and the collections module.
2
Normalise case, split into words, and count with Counter.
3
Discuss removing punctuation and stop words.
import re
from collections import Counter
def top_words(text, n=3):
words = re.findall(r"[a-z']+", text.lower())
return Counter(words).most_common(n)
Two sum: return the indices of two numbers that add up to a target.
The classic interview problem. The brute-force solution is O(n²); a dictionary of values already seen gives O(n).
def two_sum(nums, target):
seen = {}
for i, n in enumerate(nums):
if target - n in seen:
return [seen[target - n], i]
seen[n] = i
return None
two_sum([2, 7, 11, 15], 9) # [0, 1]
Write a generator for the Fibonacci series.
Shows that you understand generators and lazy evaluation. It produces numbers on demand without storing the whole series.
from itertools import islice
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
list(islice(fibonacci(), 10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Ready to test yourself?Pick your topics and level, answer by voice or text, and get instant feedback. Free.