Your language · Python
Memory and the runtime
How the runtime stores objects, garbage collection, and copies versus references.
Learn: Memory and the Python runtime9 minute read
The runtime is what Python does with your code and data once you press Run: how it executes the code, where values are stored, and how memory is freed. Interviewers ask about it because it explains everyday surprises in test code: why changing a list in one place changes it "everywhere", why a shared JSON template (a ready-made example of a request body) gets dirty between tests, and why a browser can stay open after its Python object is gone.
Step 1: how Python runs code
The interpreter is the program that runs your Python code, and the standard one is CPython. When you run or import a module, CPython compiles the source to bytecode, a set of simple instructions, and its virtual machine runs that bytecode. The bytecode of imported modules is cached in __pycache__ as .pyc files, so the next import is faster. The same .py files run on any system with a Python interpreter. Give each project its own virtual environment (python -m venv .venv) with pinned package versions, so CI runs the same Selenium and pytest versions as your machine.
Step 2: names, objects and copies
Every value, including numbers and strings, is an object on a private heap (the area of memory where objects live) that Python manages. A variable is a name bound to an object (attached to it). Each function call gets a frame (a record of one call) that holds its local names, and the frame goes away when the call ends. So Java's "objects on the heap, locals on the stack" becomes "every object on the heap, names in frames". Recursion means a function calls itself. Very deep recursion is stopped by a counter: past sys.getrecursionlimit(), 1000 by default, Python raises RecursionError (an error that means a function called itself too many times).
Because assignment only binds a name, it never copies. A shallow copy, such as d.copy(), makes a new outer object that still shares the objects inside it (the new dict and the old dict point at the same inner list). copy.deepcopy copies every level.
import copy, sys
template = {"roles": ["viewer"]}
alias = template
shallow = template.copy()
deep = copy.deepcopy(template)
template["roles"].append("admin")
print(alias["roles"], shallow["roles"], deep["roles"])
print(alias is template, shallow is template)
l1 = [1]; l2 = l1; l1 += [2]
t1 = (1,); t2 = t1; t1 += (2,)
print(l2, t2)
print(sys.getrecursionlimit())
def dive(n):
return dive(n + 1)
try:
dive(0)
except RecursionError:
print("RecursionError")
['viewer', 'admin'] ['viewer', 'admin'] ['viewer'] True False [1, 2] (1,) 1000 RecursionError
+= changes a list in place, so l2 sees the change. A tuple cannot change, so += builds a new tuple and only t1 moves to it.
Step 3: reference counting and the garbage collector
CPython frees memory in two ways. First, every object counts the references to it, and when the count drops to zero the object is freed at once. del x removes the name x; the object goes only if nothing else refers to it. Second, a cyclic garbage collector (the gc module) finds groups of objects that refer to each other but that nothing can reach, which counting alone cannot free.
import gc
class Page:
def __init__(self, name):
self.name = name
self.other = None
home, cart = Page("home"), Page("cart")
home.other, cart.other = cart, home # a reference cycle
del home, cart # names gone, objects still in a cycle
print("objects collected:", gc.collect() > 0)
print(len(gc.get_threshold()), "generation thresholds")
pages = [Page("login")]
alias = pages[0]
del pages
print("still alive through alias:", alias.name)
objects collected: True 3 generation thresholds still alive through alias: login
Python manages memory, not outside resources. Freeing a WebDriver object (the Selenium object that controls a browser) does not reliably close the browser, so call driver.quit() in fixture teardown (the clean-up part of a pytest fixture, a function that prepares things for a test).
Step 4: introspection
Reflection means inspecting and using classes and objects by name while the program runs. Python's version is called introspection. type(obj), isinstance, getattr(obj, "name", default), hasattr, vars(obj) and the inspect module are all built in. pytest (the usual Python test tool) relies on it: it collects functions whose names start with test and reads each function's parameter names, through inspect.signature, to decide which fixtures to pass in.
How to answer in an interview
Explain memory with one example: "b = a gives the list a second name, so b.append(4) shows up in a. For an independent copy I use a.copy(), and copy.deepcopy when the data is nested." For garbage collection, name both parts: reference counting frees objects at once, and the cyclic collector handles reference cycles.
Common mistakes
- Thinking assignment copies a list or dict.
- Using a shallow copy of nested test data, then changing the inner list.
- Believing
del xalways frees the object. - Comparing numbers with
is; small ints are cached only as a CPython detail, soiscan give different answers for different values. - Expecting the garbage collector to quit browsers.
Recap
CPython compiles to bytecode and runs it in its virtual machine. Every object lives on the heap, and names in frames point to them. Assignment shares, shallow copies share the inside, and deep copies share nothing. Reference counting plus a cyclic collector free memory, but you must close files and browsers yourself.
9 questions0/9 solved
| Status | Question | Difficulty | Asked in |
|---|---|---|---|
| Not started | Two names for one list | Easy | Asked in: Infosys |
| Not started | How Python runs your code: interpreter, bytecode and virtual environments | Easy-Medium | Asked in: TCS |
| Pro+ | How does Python manage memory and garbage collection? | Medium | Asked in: Accenture, TCS |
| Pro+ | Where do objects and local variables live in Python? | Medium | Asked in: Capgemini, TCS, Wipro |
| Pro+ | Shallow copy or deep copy? | Medium | Asked in: Infosys, TCS |
| Pro+ | += on a list versus a tuple | Medium | Asked in: Infosys |
| Pro+ | Why "is" with numbers is unreliable | Medium | Asked in: Capgemini, Infosys |
| Pro+ | Introspection: the Python version of reflection | Medium | Asked in: TCS |
| Pro+ | When is an object freed in CPython? | Medium | Asked in: Accenture, TCS |
The first 2 questions of each topic are free. Pro+ opens every question.