Top Python Interview Questions Interview Questions | CandidateToHR
Crack your Python backend interview. Covers GIL, decorators, generators, memory management, and advanced Python architecture.
CandidateToHR provides highly optimized, professional tech career resources. Build, customize, and analyze your tech career credentials completely free.
Master core Python. 50 real-world questions covering the GIL, Memory Management, and Object-Oriented design.
Top Interview Questions & Answers
Beginner Interview Questions
- Q: What are the key features of Python?
- A: Python is an interpreted, high-level, dynamically typed, and garbage-collected language. It supports multiple programming paradigms (object-oriented, imperative, functional) and emphasizes code readability with its significant whitespace (indentation). It has a massive standard library ('batteries included').
- Q: How is Python interpreted?
- A: Python is neither strictly interpreted nor strictly compiled. The Python source code (`.py`) is first compiled into bytecode (`.pyc`). This bytecode is then interpreted and executed by the Python Virtual Machine (PVM). This two-step process abstracts hardware dependencies.
- Q: Explain dynamically typed vs strongly typed in Python.
- A: Python is *dynamically typed*, meaning you don't declare variable types explicitly; the interpreter infers the type at runtime (e.g., `x = 10`, then `x = "hello"` is valid). However, it is *strongly typed*, meaning it won't perform implicit type coercion in dangerous ways (e.g., `"5" + 2` throws a TypeError rather than coercing to 7 or '52').
- Q: What is the difference between a list and a tuple?
- A: Lists are mutable (can be changed after creation) and defined using square brackets `[]`. Tuples are immutable (cannot be changed after creation) and defined using parentheses `()`. Because tuples are immutable, they are faster, have a smaller memory footprint, and can be used as keys in dictionaries.
- Q: What is a dictionary in Python?
- A: A dictionary is a built-in data type that stores data in key-value pairs. It is mutable, unordered (though Python 3.7+ preserves insertion order), and keys must be unique and immutable (like strings, numbers, or tuples). Defined using curly braces `{key: value}`.
- Q: How do you handle exceptions in Python?
- A: Exceptions are handled using the `try-except-else-finally` block. Code that might throw an error goes in `try`. The `except` block catches specific exceptions. `else` runs only if no exception occurred. `finally` runs regardless of whether an exception occurred, usually used for cleanup (closing files/connections).
- Q: What is the purpose of `__init__` in Python classes?
- A: `__init__` is a special 'dunder' (double underscore) method acting as the constructor for a class. It is automatically called when a new instance of the class is created. It initializes the object's attributes and assigns the initial state.
- Q: What does `self` mean in Python?
- A: `self` represents the instance of the class. By using the `self` keyword inside class methods, we can access the attributes and methods of the class. Unlike `this` in C++ or Java, `self` is not a reserved keyword but a strong naming convention (you must pass it explicitly as the first parameter of instance methods).
- Q: Explain `*args` and `**kwargs`.
- A: `*args` (Non-Keyword Arguments) allows a function to accept a variable number of positional arguments as a tuple. `**kwargs` (Keyword Arguments) allows a function to accept a variable number of keyword arguments as a dictionary. They provide immense flexibility in function definitions.
- Q: What are list comprehensions?
- A: List comprehensions provide a concise syntax for creating new lists based on the values of an existing iterable. Syntax: `[expression for item in iterable if condition]`. They are generally faster and more readable than equivalent `for` loops with `.append()`.
- Q: What is a Lambda function?
- A: A lambda function is a small, anonymous, single-expression function defined using the `lambda` keyword. Syntax: `lambda arguments: expression`. They are often used as throwaway functions for higher-order functions like `map()`, `filter()`, or `sorted()`.
- Q: What is the difference between `==` and `is`?
- A: `==` checks for value equality (do these objects hold the same data?). `is` checks for reference equality or identity (do these variables point to the exact same memory location?).
- Q: What are modules and packages in Python?
- A: A module is a single Python file (`.py`) containing functions, classes, and variables. A package is a directory containing multiple modules and a special `__init__.py` file (though optional in Python 3.3+), which allows the directory to be treated as an importable module.
- Q: How do you open and close a file safely?
- A: The safest way is using the `with` statement (a context manager). Example: `with open('file.txt', 'r') as f: data = f.read()`. The context manager ensures that the file is automatically closed when the block exits, even if an exception occurs.
- Q: What is `pass` in Python?
- A: The `pass` statement is a null operation. It serves as a placeholder when a statement is syntactically required but you don't want any code to execute (e.g., in empty classes, functions, or `except` blocks during scaffolding).
- Q: What are Python sets?
- A: Sets are unordered collections of unique elements. Defined using `{}` or `set()`. They are highly optimized for membership testing (`in` operator) and mathematical set operations like union, intersection, and difference.
- Q: What does `break`, `continue`, and `pass` do in loops?
- A: `break` exits the loop entirely. `continue` skips the rest of the code inside the current iteration and jumps to the next iteration. `pass` does absolutely nothing and just acts as a structural placeholder.
Intermediate Interview Questions
- Q: What are generators and the `yield` keyword?
- A: Generators are a simple way of creating iterators. Instead of returning a massive array all at once (consuming huge RAM), a generator function uses the `yield` keyword to return one value at a time, pausing its state between calls. This makes generators incredibly memory-efficient for reading massive files or infinite streams.
- Q: Explain Python Decorators.
- A: A decorator is a design pattern that allows you to modify the behavior of a function or class without permanently changing its source code. In Python, decorators are higher-order functions that take a function as an argument, wrap it with additional logic, and return the wrapper. They are applied using the `@decorator_name` syntax above a function.
- Q: What is the Global Interpreter Lock (GIL)?
- A: The GIL is a mutex in CPython that protects access to Python objects, preventing multiple native threads from executing Python bytecodes simultaneously. This makes CPython thread-safe but prevents true parallel execution in CPU-bound multi-threaded programs. (Note: The GIL does not heavily restrict I/O-bound multithreading).
- Q: How do you achieve true concurrency in Python given the GIL?
- A: To bypass the GIL for CPU-bound tasks, you must use the `multiprocessing` module instead of `threading`. Multiprocessing spawns entirely separate OS processes, each with its own Python interpreter and its own memory space (and thus its own GIL), allowing true parallel execution across multiple CPU cores.
- Q: What are context managers?
- A: Context managers allocate and release resources precisely when you want to. The most widely used example is the `with` statement. You can write custom context managers using classes by defining `__enter__` and `__exit__` dunder methods, or by using the `@contextmanager` decorator from the `contextlib` module.
- Q: Explain `__new__` vs `__init__`.
- A: `__new__` is the actual constructor method responsible for *creating* and returning a new instance of a class. `__init__` is the initializer method responsible for setting up the instance's state *after* it has been created. You rarely override `__new__` unless subclassing immutable types (like tuple/str) or implementing the Singleton pattern.
- Q: What is monkey patching?
- A: Monkey patching refers to dynamically modifying a class or module at runtime. For example, you can overwrite a function in a third-party library with your own function without altering the original source code. While powerful for testing (mocking), it can lead to difficult-to-debug code in production.
- Q: What are deep copy and shallow copy?
- A: A shallow copy (`copy.copy()`) creates a new object but inserts *references* to the objects found in the original. A deep copy (`copy.deepcopy()`) creates a new object and recursively inserts *copies* of the objects found in the original. Mutating nested lists inside a shallow copy affects the original; mutating a deep copy does not.
- Q: Explain duck typing in Python.
- A: Duck typing is a concept related to dynamic typing: 'If it walks like a duck and quacks like a duck, it must be a duck.' In Python, you don't check the specific type of an object to see if it's valid; you just check if it implements the required methods (e.g., trying to call `.read()` on an object assumes it's a file-like object, regardless of its actual class).
- Q: What are metaclasses?
- A: If objects are instances of classes, classes are instances of metaclasses. A metaclass defines the behavior of a class. The default metaclass in Python is `type`. You can write custom metaclasses to automatically modify class attributes, enforce coding standards, or register classes at runtime before the class object is fully created.
- Q: What are dunder (magic) methods?
- A: Dunder methods are special methods with double underscores (e.g., `__str__`, `__len__`, `__add__`). They allow you to define how custom objects interact with built-in Python operators and functions. For example, implementing `__len__` allows your object to work with the `len()` function.
- Q: What is the difference between `staticmethod` and `classmethod`?
- A: A `@classmethod` takes the class itself (`cls`) as its first argument, allowing it to modify class state that applies across all instances (like tracking instance counts or alternative constructors). A `@staticmethod` takes neither `self` nor `cls`. It behaves like a plain function but is placed inside a class's namespace for logical grouping.
- Q: What is string interning?
- A: String interning is a memory optimization technique where Python stores only one copy of distinct string values. Short, identifier-like strings (like 'hello') are automatically interned. Thus, two variables assigned the identical short string will point to the exact same memory address (`is` returns True), saving memory and speeding up comparisons.
- Q: How does `map()` differ from list comprehensions?
- A: `map()` applies a function to all items in an input list and returns an iterator (in Python 3), so it uses less memory until evaluated. A list comprehension evaluates immediately and returns a fully populated list. List comprehensions are generally more readable and slightly faster when combined with a lambda.
- Q: What is the `enumerate()` function?
- A: `enumerate()` adds a counter to an iterable and returns it as an enumerate object. It is heavily used in `for` loops when you need both the index and the value simultaneously: `for index, value in enumerate(my_list):`.
- Q: Explain Name Mangling in Python.
- A: Python does not have strict private modifiers. However, prefixing an attribute with double underscores `__` triggers 'name mangling'. Python interpreter rewrites the attribute name to `_ClassName__attributeName` to prevent accidental access or overriding in subclasses. It is a safeguard, not true security.
- Q: What is the LEGB rule?
- A: LEGB defines the scope resolution order in Python: Local (inside current function), Enclosing (inside any enclosing functions), Global (top level of the module), and Built-in (pre-assigned by Python like `len()`). Python searches for a variable in this exact order.
Advanced Interview Questions
- Q: Explain Python's Garbage Collection mechanism in detail.
- A: Python uses two mechanisms: Reference Counting and a Generational Garbage Collector. Reference counting tracks how many references point to an object; when it hits zero, the memory is instantly freed. However, reference counting cannot resolve reference cycles (e.g., Object A references B, and B references A). To fix this, Python runs a background Generational GC that periodically sweeps memory to detect and break isolated reference cycles.
- Q: What is `asyncio` and how does the Event Loop work in Python?
- A: `asyncio` is a library for writing concurrent code using the `async/await` syntax. It revolves around an Event Loop that manages and distributes execution of different tasks. When a coroutine awaits an I/O operation (like an HTTP request), it yields control back to the Event Loop, which seamlessly runs other tasks until the I/O operation completes, vastly improving throughput for networking apps.
- Q: How do you implement the Singleton pattern in Python?
- A: A common way is by overriding the `__new__` method. Create a class attribute `_instance`. Inside `__new__`, check if `cls._instance` exists. If not, create it via `super().__new__(cls)` and store it. Otherwise, return the existing `_instance`. Another robust pythonic way is utilizing a metaclass or simply defining the logic in a separate module (since Python modules are inherently singletons when imported).
- Q: What are descriptors in Python?
- A: Descriptors are classes that implement one or more of the descriptor protocols: `__get__`, `__set__`, or `__delete__`. When assigned as a class attribute, a descriptor overrides default attribute access. This is the underlying magic behind `@property`, `@classmethod`, `@staticmethod`, and SQLAlchemy ORM attributes.
- Q: What is Multiple Inheritance and the Diamond Problem (MRO)?
- A: Python supports multiple inheritance. The Diamond Problem occurs when Class D inherits from B and C, which both inherit from A. If D calls a method defined in A (and overridden in B and C), which one executes? Python solves this using the C3 Linearization algorithm to create a Method Resolution Order (MRO). You can view the order via `ClassName.__mro__`.
- Q: How would you optimize a Python application consuming too much memory?
- A: 1) Use generators instead of lists. 2) Use `__slots__` in classes to prevent the creation of `__dict__` for every instance, significantly reducing RAM. 3) Use memory-mapped files (`mmap`) for large datasets. 4) Use specialized libraries like NumPy which store data in contiguous C-arrays instead of fragmented Python objects. 5) Profile using `memory_profiler` or `tracemalloc` to find leaks.
- Q: Explain `functools.lru_cache`.
- A: `@lru_cache` (Least Recently Used Cache) is a decorator that memoizes the result of a function. If the function is called again with the same arguments, it returns the cached result, saving computation time. 'Least recently used' means that when the cache hits its `maxsize`, it discards the oldest, least accessed item to free up memory.
- Q: What are type hints and `mypy`?
- A: Introduced in PEP 484, type hints (`def greet(name: str) -> str:`) allow developers to annotate expected types. Python strictly ignores them at runtime (it remains dynamically typed). However, static analysis tools like `mypy` can read these annotations during CI/CD pipelines to catch type-mismatch bugs before deployment, bridging the gap between dynamic and static typing.
- Q: Explain the mechanics of Python's `import` system.
- A: When you import a module, Python 1) checks `sys.modules` to see if it's already cached. 2) If not, it searches the directories in `sys.path` (current dir, standard library, site-packages). 3) It compiles the `.py` to `.pyc`. 4) It executes the module's code in a new namespace. 5) It adds the module to `sys.modules`. This is why cyclic imports can cause failure—the module is executing before fully loading.
- Q: What is Cython?
- A: Cython is a superset of Python that compiles directly to C code, which is then compiled to machine code. By adding static C-type declarations to Python code, Cython bypasses Python's dynamic overhead and the GIL, achieving execution speeds comparable to pure C, making it perfect for mathematical bottlenecks.
- Q: How do you handle cyclic imports in Python?
- A: Cyclic imports occur when module A imports B, and B imports A. Fixes include: 1) Refactoring the shared logic into a neutral module C. 2) Moving the `import` statement to the bottom of the file (hacky). 3) Moving the `import` statement inside the specific function that needs it, deferring the import until runtime execution.
- Q: Explain the difference between `__getattr__` and `__getattribute__`.
- A: `__getattribute__` is called *every* time any attribute is accessed. If it throws an AttributeError, Python falls back and calls `__getattr__`. Thus, `__getattr__` is only invoked as a fallback for missing attributes, while overriding `__getattribute__` intercepts all attribute access (and can easily cause infinite recursion if you don't call `super().__getattribute__`).
- Q: What is pickling and unpickling? What are the security risks?
- A: Pickling (`pickle` module) serializes Python object structures into a binary format for saving to disk or sending over a network. Unpickling deserializes it. Security Risk: Unpickling arbitrary data is highly dangerous. A maliciously crafted pickle payload can execute arbitrary shell commands upon unpickling. Never unpickle data from untrusted sources.
- Q: How do you implement a generic coroutine pipeline?
- A: Coroutines in Python (using `yield` as an expression `val = yield`) can receive data via `.send()`. A pipeline consists of a source (produces data), filters (receives data, transforms it, sends it to the next), and a sink (final destination). You initialize coroutines using `next(coroutine)` to prime them up to the first yield, then chain them using `.send()`.
- Q: What are data classes?
- A: Introduced in Python 3.7 (`@dataclass`), they are a decorator that automatically generates boilerplate methods like `__init__`, `__repr__`, and `__eq__` based on class variables annotated with type hints. They dramatically reduce boilerplate code when creating classes that primarily store data.
- Q: Explain the inner workings of Python dictionaries.
- A: Dictionaries are implemented as Hash Tables. When inserting a key-value pair, Python computes the `hash()` of the key to determine the memory index. If a collision occurs (two keys yield the same index), Python uses Open Addressing with a pseudo-random probing sequence to find the next empty slot. In Python 3.7+, a separate dense array maintains insertion order, while a sparse hash array stores indices.
Frequently Asked Questions
How many questions are covered in this guide?
This guide covers 50+ of the most frequently asked questions.
Are these questions suitable for beginners?
Yes, the guide is divided into beginner, intermediate, and advanced sections.
How often is this guide updated?
We update our interview questions quarterly to ensure they reflect current industry standards.
Should I memorize the answers?
No, it is better to understand the underlying concepts rather than memorizing answers word-for-word.
Are these questions asked at FAANG companies?
Yes, many of these questions are standard in interviews at top tech companies like Google, Amazon, and Meta.
Related Resources & Next Steps