This guide covers core concepts, memory models, and standard questions frequently asked during senior and mid-level Python developer interviews.
- Immutable Types: Once created, their value cannot be changed. Any operation that alters the value results in the creation of a new object in memory.
- Examples:
int,float,str,tuple,frozenset,bool.
- Examples:
- Mutable Types: Their internal state or value can be changed in place without changing the object's identity (memory address).
- Examples:
list,dict,set,bytearray.
- Examples:
- Key Interview Gotcha: Modifying a list passed into a function alters the caller's list because lists are passed by assignment (reference sharing).
def append_item(lst=[]): # WARNING: Mutable default arguments are evaluated ONCE at function definition! lst.append("item") return lst
Python uses two main mechanisms for memory allocation and cleanup:
- Reference Counting:
- Every Python object keeps track of how many variables or collections reference it.
- When an object's reference count drops to 0, its memory is immediately deallocated.
- Garbage Collector (Generational GC):
- Used to detect and clean up reference cycles (e.g., Object A references Object B, and Object B references Object A, but both are unreachable from the root scope).
- Python categorizes objects into 3 generations (Gen 0, 1, and 2) based on how long they have survived. Gen 0 is collected most frequently.
- Definition: The GIL is a mutex (lock) in the standard CPython implementation that prevents multiple native threads from executing Python bytecodes at once.
- Why does it exist? It simplifies CPython implementation by making memory management (specifically reference counting) thread-safe without needing granular locking.
- How to bypass the GIL?
- Use the
multiprocessingmodule (creates separate OS processes, each with its own interpreter and GIL). - Outsource heavy computations to C extensions (like
numpy,pandas) which release the GIL during execution. - Use alternative interpreters like Jython or IronPython (which do not have a GIL).
- Use the
-
Iterable: An object that can return its elements one at a time (implements
__iter__()). -
Iterator: An object that implements
__next__()and__iter__(). It keeps track of its current state and raisesStopIterationwhen elements run out. -
Generator: A simpler way to write an iterator using a function and the
yieldkeyword.- Instead of computing all values upfront and storing them in memory (like a list), a generator yields values lazily on demand.
-
Memory Efficiency: Generating 1 million items sequentially via a generator takes
$O(1)$ memory, whereas a list takes$O(n)$ .
- A decorator is a function that takes another function as an argument, adds some behavior, and returns a new function without modifying the original function's source code.
- It leverages closures to retain access to variables in the outer enclosing scope.
- Basic template:
def my_decorator(func): def wrapper(*args, **kwargs): # Do something before func runs result = func(*args, **kwargs) # Do something after return result return wrapper
"If it walks like a duck and quacks like a duck, it's a duck."
- Python does not check types at compile-time. Instead, it checks for the presence of methods or properties at runtime.
- If an object has a
.read()method, Python will treat it as a file-like object, regardless of whether it inherits fromio.IOBase.
==compares values (calls__eq__under the hood) to check if they are equivalent.iscompares identities (memory addresses usingid()) to check if they refer to the exact same object in memory.a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True (same values) print(a is b) # False (different objects in memory)