§ Hiring Tips·25 min read·October 3, 2026

15 Python Job Interview Questions with Answers and Code

O
Olibr TeamHiring Tips
§ Contents
15 Python Job Interview Questions with Answers and Code1. What is the difference between a list and a tuple?Short answerCode exampleFollow-up questions interviewers ask2. Is Python compiled or interpreted?Short answerCode exampleFollow-up questions interviewers ask3. What are mutable and immutable data types?Short answerCode exampleFollow-up questions interviewers ask4. What is the difference between == and is?Short answerCode exampleFollow-up questions interviewers ask5. How do dictionaries and sets differ?Short answerCode exampleFollow-up questions interviewers ask6. What are list comprehensions and how do they work?Short answerCode exampleFollow-up questions interviewers ask7. What are *args and **kwargs?Short answerCode exampleFollow-up questions interviewers ask8. How does variable scope and the LEGB rule work?Short answerCode exampleFollow-up questions interviewers ask9. What is the difference between shallow and deep copy?Short answerCode exampleFollow-up questions interviewers ask10. What are decorators in Python?Short answerCode exampleFollow-up questions interviewers ask11. What are iterators and generators?Short answerCode exampleFollow-up questions interviewers ask12. How does exception handling work in Python?Short answerCode exampleFollow-up questions interviewers ask13. How does OOP work: classes, inheritance and self?Short answerCode exampleFollow-up questions interviewers ask14. What is the difference between classmethod and staticmethod?Short answerCode exampleFollow-up questions interviewers ask15. What is the Global Interpreter Lock (GIL)?Short answerCode exampleFollow-up questions interviewers ask16. How does Python manage memory?Short answerCode exampleFollow-up questions interviewers askFinal tips for your Python interview
15 Python Job Interview Questions with Answers and Code

15 Python Job Interview Questions with Answers and Code

Walking into a Python interview without a clear idea of what gets asked is how strong developers stumble on simple questions. Most rounds repeat the same core topics: data types, functions, object-oriented design, and a few tricky behaviors that catch people out. This guide covers python job interview questions that come up again and again, so you can practice the right things instead of memorizing random trivia.

Here is the direct answer. You need to explain mutable versus immutable types, decorators, generators, and the GIL in plain words, and write short code to prove it. Interviewers care less about textbook definitions and more about whether you can reason through an example.

Below you will find 15 questions that move from basics to advanced topics. Each one has a short answer and a working code snippet you can run yourself. If you are a recruiter or hiring manager reading this, it also works as a screening reference. At Olibr, we help recruiting teams reuse candidate data and screen faster, and a solid question bank makes that screening sharper.

1. What is the difference between a list and a tuple?

Short answer

A list is mutable, so you can add, remove, or change items after you create it. A tuple is immutable, so its contents are fixed once it exists. That single difference drives the rest: speed and memory use, whether the object can be a dictionary key, and where each one belongs in real code.

Feature List Tuple
Syntax [1, 2, 3] (1, 2, 3)
Mutable Yes No
Hashable No Yes, if every item is hashable
Memory Slightly larger Slightly smaller
Typical use Collections that change Fixed records, dictionary keys

Use a list when the data will change, and a tuple when it should never change.

Code example

Run this snippet and read each result before you move on. It shows item assignment failing on a tuple, and it shows why a tuple works as a dictionary key while a list does not.

nums = [1, 2, 3]
nums[0] = 10
nums.append(4)
print(nums)  # [10, 2, 3, 4]

point = (1, 2, 3)
try:
    point[0] = 10
except TypeError as e:
    print(e)  # 'tuple' object does not support item assignment

locations = {(28.61, 77.20): "Delhi"}  # tuple key works
# {[28.61, 77.20]: "Delhi"} raises TypeError: unhashable type: 'list'

Follow-up questions interviewers ask

Expect the conversation to go deeper, because this is one of the most common python interview questions and the definition alone proves very little. Interviewers use it to check that you understand identity and hashing, not just syntax. Be ready for these:

  • Can a tuple hold a mutable object? Yes. In ([1], 2) you can still append to the inner list. The tuple only guarantees that it keeps pointing to the same objects.
  • How do you write a one-item tuple? Use a trailing comma: (5,). Without it, (5) is just the integer 5.
  • Why are tuples faster to create? Python can store constant tuples ahead of time, and tuples carry no extra space for resizing.
  • When would you pick a tuple over a list? For fixed records such as (name, age), function return values, and dictionary keys.

2. Is Python compiled or interpreted?

Short answer

Three-step diagram showing source file, bytecode compilation, and virtual machine execution in Python.

It is both, and that is the answer interviewers want to hear. CPython compiles your source code to bytecode, and then the Python virtual machine interprets that bytecode one instruction at a time. Calling Python "interpreted" is shorthand for this process, not a property of the language itself. The flow looks like this:

  1. You write a .py source file.
  2. CPython compiles it to bytecode, cached as .pyc files in __pycache__.
  3. The virtual machine executes the bytecode.

Python compiles to bytecode first, and the virtual machine then interprets that bytecode.

Code example

The built-in dis module shows the bytecode behind any function. Running it is a strong move when you answer interview questions about Python internals, because it proves you have looked under the hood instead of repeating a definition.

import dis

def add(a, b):
    return a + b

dis.dis(add)
# Prints instructions such as LOAD_FAST a, LOAD_FAST b,
# BINARY_OP (+) and RETURN_VALUE. Exact names vary by version.

Follow-up questions interviewers ask

This topic often leads to performance and implementation questions, so prepare short, concrete answers for the points below. Then you can show that you know where bytecode and speed connect.

  • What is a .pyc file? It is cached bytecode. Python reuses it when the source has not changed, which makes imports start faster. It does not make the program itself run faster.
  • Why is Python slower than C? The virtual machine adds overhead for every instruction, and types are checked at runtime.
  • Can Python be compiled further? Yes. PyPy uses a just-in-time compiler, and Cython compiles Python-like code to C.
  • Does the implementation matter? Yes. CPython, PyPy, and Jython all run the same language differently.

3. What are mutable and immutable data types?

Short answer

Mutable objects can change in place after you create them. Immutable objects cannot, so any "change" builds a brand new object instead. Lists, dictionaries, and sets are mutable. Integers, floats, strings, tuples, and frozensets are immutable.

Mutable objects change in place, while immutable objects get replaced by new ones.

Code example

Check id() before and after each change. The list keeps the same id after append, but the string gets a different id because Python rebuilds it. The last part shows the trap that most candidates miss.

items = [1, 2]
print(id(items))
items.append(3)
print(id(items))   # same id, changed in place

name = "py"
print(id(name))
name += "thon"
print(id(name))    # different id, new object

def add(x, bucket=[]):
    bucket.append(x)
    return bucket

print(add(1))  # [1]
print(add(2))  # [1, 2], the default list is shared

Follow-up questions interviewers ask

Interviewers use this topic to test whether you understand side effects in functions, which makes it a favorite among python job interview questions. Prepare for these:

  • What is the mutable default argument trap? Python creates the default value once, at definition time, so every call shares it. Use None as the default and build a new list inside the function.
  • Are strings mutable? No. name[0] = "P" raises a TypeError.
  • Why must dictionary keys be immutable? Keys need a stable hash, and a value that changes in place would break the lookup.

4. What is the difference between == and is?

Short answer

The == operator checks value equality, while is checks identity, meaning whether two names point to the exact same object in memory. Two separate lists with the same items are equal, but they are not identical. Use == to compare data, and use is for singletons such as None.

Use == to compare values, and use is only to check that two names share one object.

Code example

Here two lists match in value but live at different memory locations. The last lines show small integer caching, which surprises many candidates, so treat it as an implementation detail and never rely on it.

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True, same values
print(a is b)  # False, different objects
print(a is c)  # True, same object

x = None
print(x is None)  # True, the correct check

m = int("1000")
n = int("1000")
print(m == n)  # True
print(m is n)  # False in CPython, do not depend on this

Follow-up questions interviewers ask

Few interview questions on python expose shaky fundamentals as fast as this one, so expect a follow-up on None checks and caching. Prepare for these:

  • Why write is None instead of == None? None is a singleton, and a class can override __eq__, so == may return a misleading result.
  • Why does is sometimes return True for small integers? CPython caches integers from -5 to 256, so those values share one object.
  • Can you change how == behaves? Yes, define __eq__ on your class. You cannot override is.
  • What does is compare under the hood? In CPython, it is the same as id(a) == id(b).

5. How do dictionaries and sets differ?

Short answer

A dictionary stores key-value pairs, while a set stores unique values with no keys. Both rely on hashing, so lookups and membership checks take O(1) time on average. Since Python 3.7, dictionaries keep insertion order. Sets guarantee no order at all.

Feature Dictionary Set
Syntax {"a": 1} {1, 2, 3}
Stores Key-value pairs Unique items
Duplicates Keys unique, values may repeat Not allowed
Typical use Lookup by key Membership tests, removing duplicates

A dictionary maps keys to values, and a set only remembers which values exist.

Code example

This snippet covers lookup, duplicate removal, and set math. Watch the empty braces, because {} creates an empty dictionary, not a set.

ages = {"Asha": 29, "Ravi": 34}
print(ages["Asha"])        # 29

skills = ["sql", "python", "sql"]
unique = set(skills)
print(unique)              # {'sql', 'python'}

a, b = {1, 2, 3}, {2, 3, 4}
print(a & b)               # {2, 3}
print(a | b)               # {1, 2, 3, 4}

print(type({}))            # <class 'dict'>
empty = set()              # the correct empty set

Follow-up questions interviewers ask

Expect interviewers to probe hashing and speed next, so keep short, concrete answers ready for these:

  • Why is x in my_set faster than x in my_list? A set jumps straight to the hash bucket. A list scans every item, which is O(n).
  • How do you create an empty set? Call set(). Braces give you a dictionary.
  • Can a set contain a list? No, because lists are unhashable. Use a tuple or a frozenset instead.
  • How do you merge two dictionaries? Use a | b in Python 3.9 and later, or {**a, **b} in older versions.

6. What are list comprehensions and how do they work?

Short answer

A list comprehension is a compact way to build a new list from any iterable, with an optional filter. The pattern is [expression for item in iterable if condition]. It replaces a loop plus append, and it often runs slightly faster too. Keep it for simple transforms, and switch to a plain loop when the logic gets nested.

A list comprehension builds a new list in one expression, so use it for simple transforms and filters.

Code example

The snippet compares a loop with its comprehension, then adds a filter and a nested loop. It ends with the dictionary and set variants that show up in python language interview questions.

squares = []
for n in range(5):
    squares.append(n * n)

squares = [n * n for n in range(5)]            # [0, 1, 4, 9, 16]
evens = [n for n in range(10) if n % 2 == 0]   # [0, 2, 4, 6, 8]
pairs = [(x, y) for x in range(2) for y in range(2)]
# [(0, 0), (0, 1), (1, 0), (1, 1)]

lengths = {w: len(w) for w in ["sql", "python"]}  # dict comprehension
remainders = {n % 3 for n in range(10)}            # set comprehension

Follow-up questions interviewers ask

Interviewers usually move from syntax to memory and readability, so prepare short answers for these:

  • Is a comprehension faster than a loop? Usually yes, because it skips the repeated append lookup. The gain is small, so choose it for readability, not speed.
  • How does it differ from a generator expression? Square brackets build the whole list in memory. Round brackets, as in (n * n for n in range(5)), produce items lazily.
  • Does the loop variable leak out? Not in Python 3. The variable stays inside the comprehension's own scope.
  • Where does an if go? A filter goes at the end: [x for x in data if x]. A conditional expression goes in front: [x if x else 0 for x in data].

7. What are *args and **kwargs?

Short answer

The name *args collects extra positional arguments into a tuple. The name **kwargs collects extra keyword arguments into a dictionary. Only the stars matter, and the names are convention. Use them when you do not know how many arguments callers will pass, as in wrappers and decorators. A signature must follow this order: regular parameters, *args, keyword-only parameters, then **kwargs.

*args gives you a tuple of positional values, and **kwargs gives you a dictionary of named ones.

Code example

This snippet shows both collecting arguments in a definition and unpacking them in a call. Many interview questions about Python functions start here, so run it yourself.

def report(title, *args, **kwargs):
    print(title)
    print(args)    # (1, 2, 3)
    print(kwargs)  # {'sep': '-', 'end': '!'}

report("Scores", 1, 2, 3, sep="-", end="!")

nums = [1, 2, 3]
opts = {"sep": "-"}
print(*nums, **opts)  # 1-2-3

def only_keywords(a, *, b):  # b must be passed by name
    return a + b

Follow-up questions interviewers ask

Interviewers often check whether you know unpacking and keyword-only arguments, so keep these answers ready:

  • Can you use other names? Yes. *items and **options work the same way.
  • What does * do in a call? It unpacks an iterable into separate positional arguments. ** does the same for a dictionary.
  • How do you force keyword-only arguments? Place a bare * in the signature, as in def f(a, *, b).
  • Why do decorators use them? A wrapper written as wrapper(*args, **kwargs) forwards any call to the original function unchanged.

8. How does variable scope and the LEGB rule work?

Short answer

Scope decides where a name is visible in your program. Python looks a name up in four places, in this order: Local, Enclosing, Global, then Built-in. This is the LEGB rule, and the search stops at the first match.

Python resolves every name by checking Local, Enclosing, Global, then Built-in, and it stops at the first hit.

Code example

Each print below shows which layer wins. The second half shows the UnboundLocalError trap, which turns up in many interview questions about Python functions, along with the fix using global and nonlocal.

x = "global"

def outer():
    x = "enclosing"
    def inner():
        x = "local"
        print(x)   # local
    inner()
    print(x)       # enclosing

outer()
print(x)           # global
print(len([1, 2])) # len comes from the built-in scope

count = 0
def bad():
    count += 1     # UnboundLocalError

def good():
    global count
    count += 1

def counter():
    n = 0
    def step():
        nonlocal n
        n += 1
        return n
    return step

Follow-up questions interviewers ask

Interviewers like this topic because one small mistake with assignment reveals how well you know the rules. Prepare for these:

  • Why does count += 1 fail? Assignment makes the name local for the whole function, so Python reads it before it exists.
  • What is the difference between global and nonlocal? global targets the module level. nonlocal targets the nearest enclosing function.
  • Do if and for blocks create a new scope? No. Only functions, classes, modules, and comprehensions do.
  • What is a closure? An inner function that remembers variables from its enclosing function, even after that function returns.

9. What is the difference between shallow and deep copy?

Short answer

Two nested box arrangements, one sharing inner boxes with the original and one with fully separate inner boxes.

A shallow copy builds a new outer container but reuses the same inner objects. A deep copy recursively duplicates everything, so nothing is shared with the original. The difference only matters when your data holds nested mutable objects, such as a list of lists.

Copy type Outer object Nested objects
Assignment (b = a) Same Same
Shallow copy New Shared
Deep copy New New

A shallow copy duplicates the container, and a deep copy duplicates the container and everything inside it.

Code example

The snippet changes an inner list after copying. The shallow copy shows the change, while the deep copy stays untouched.

import copy

original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original[0].append(99)

print(shallow)                    # [[1, 2, 99], [3, 4]]
print(deep)                       # [[1, 2], [3, 4]]
print(shallow is original)        # False
print(shallow[0] is original[0])  # True, inner list is shared

Follow-up questions interviewers ask

Copying is a staple of interview questions about Python references, so expect a few probing follow-ups. Keep short, concrete answers ready for these:

  • Does b = a make a copy? No. It binds a second name to the same object.
  • Which operations are shallow? list.copy(), dict.copy(), slicing with [:], and list(a) all make shallow copies.
  • Can deepcopy handle circular references? Yes. It tracks objects it has already copied in a memo dictionary.
  • What does a deep copy cost? More time and memory. You can customize it by defining __deepcopy__ on your class.

10. What are decorators in Python?

Short answer

A decorator is a function that takes another function and returns a new one, so you can add behavior without editing the original code. The @name line above a def is shorthand for func = name(func). It works because functions are objects you can pass around. Typical uses are logging, timing, caching, and access checks.

A decorator wraps a function to extend what it does, and leaves the original function's code alone.

Code example

This timing decorator is the version most interviewers expect. It uses *args and **kwargs to forward any call, and functools.wraps to keep the original name and docstring.

import functools
import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

@timer
def slow_sum(n):
    return sum(range(n))

print(slow_sum(1_000_000))
print(slow_sum.__name__)  # slow_sum, thanks to wraps

Follow-up questions interviewers ask

Decorators sit near the top of most interview questions on python, because they combine closures, scope, and first-class functions in one answer. Prepare for these follow-ups:

  • Why use functools.wraps? Without it, the wrapper replaces the original function's __name__ and docstring, which breaks debugging tools.
  • How do you pass arguments to a decorator? Add one more layer. A function takes the arguments and returns the decorator, as in @retry(times=3).
  • What happens when you stack decorators? They apply from the bottom up, so the one closest to the def runs first.
  • Which built-in decorators should you know? @property, @staticmethod, @classmethod, and functools.lru_cache.

11. What are iterators and generators?

Short answer

An iterator is any object with __iter__ and __next__ methods. It hands you one value per call and raises StopIteration when it runs out. A generator is the shortest way to write an iterator. You put yield inside a function, and Python pauses it and resumes it where it stopped. Generators produce values lazily, so they use almost no memory, even on huge data.

An iterator gives you one item at a time, and a generator is the simplest way to build one.

Code example

The snippet below is a classic from python programming questions interviews. It yields Fibonacci numbers without building a list, then calls next() by hand to show the protocol underneath.

def fib(limit):
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b

gen = fib(20)
print(next(gen))   # 0
print(next(gen))   # 1
print(list(gen))   # [1, 2, 3, 5, 8, 13]

total = sum(n * n for n in range(1_000_000))  # generator expression

Follow-up questions interviewers ask

Interviewers usually test exhaustion and memory use next, so keep these answers ready:

  • Can you loop over a generator twice? No. It is exhausted after one pass, so you must create a new one.
  • What is the difference between an iterable and an iterator? A list is iterable. Calling iter() on it returns an iterator.
  • What does yield from do? It hands control to another iterable and passes its values straight through.
  • When should you pick a generator over a list? For large or streaming data, such as reading a file line by line.

12. How does exception handling work in Python?

Short answer

Python handles errors with try, except, else, and finally blocks. Code that might fail goes in try. A matching except catches the error, else runs only if nothing went wrong, and finally always runs, so it is the right place for cleanup. Catch the specific exception you expect, never a bare except:.

Catch the specific error you expect, and let everything else fail loudly.

Code example

This snippet uses all four blocks and raises a custom exception. Many interview questions about Python error handling ask you to write exactly this.

class InvalidAge(Exception):
    pass

def parse_age(text):
    try:
        age = int(text)
        if age < 0:
            raise InvalidAge("age cannot be negative")
    except ValueError:
        print("not a number")
    except InvalidAge as e:
        print(e)
    else:
        print("valid:", age)
    finally:
        print("done")

parse_age("29")   # valid: 29, then done
parse_age("abc")  # not a number, then done
parse_age("-5")   # age cannot be negative, then done

Follow-up questions interviewers ask

Interviewers push on best practices here, so prepare short answers for these:

  • Why avoid a bare except:? It also swallows KeyboardInterrupt and hides real bugs.
  • What does raise ... from do? A plain raise re-raises the current error. Adding from chains exceptions so the root cause stays visible.
  • Does finally run after a return? Yes. It runs before the function actually returns.
  • What is a context manager? A with statement closes files and releases locks for you, even when an error occurs.

13. How does OOP work: classes, inheritance and self?

Short answer

A class is a blueprint for objects, and each instance holds its own data. Inheritance lets a child class reuse and override a parent's methods. The name self is the instance the method was called on, which Python passes in automatically as the first argument.

A class defines the blueprint, an instance holds the data, and self connects the two.

Code example

The snippet shows __init__, a child class, super(), and overriding. Note that one call behaves differently depending on the object's class.

class Employee:
    def __init__(self, name):
        self.name = name

    def role(self):
        return f"{self.name} is an employee"

class Manager(Employee):
    def __init__(self, name, team):
        super().__init__(name)
        self.team = team

    def role(self):  # overrides the parent
        return f"{self.name} manages {len(self.team)}"

for p in [Employee("Asha"), Manager("Ravi", ["Asha"])]:
    print(p.role())  # polymorphism in action

Follow-up questions interviewers ask

Expect probing on method resolution and encapsulation, a common theme in interview questions in python programming. Prepare for these:

  • Is self a keyword? No. It is a convention, but never rename it.
  • What does super() do? It calls the next class in the method resolution order, which you can inspect with Manager.__mro__.
  • Does Python support multiple inheritance? Yes. The MRO uses C3 linearization to pick a predictable order.
  • Are there private attributes? No. A single underscore signals "internal", and a double underscore triggers name mangling.

14. What is the difference between classmethod and staticmethod?

Short answer

A @classmethod receives the class itself as its first argument, usually named cls. A @staticmethod receives no automatic argument, so it behaves like a plain function that happens to live inside the class. A regular instance method gets self.

Method type First argument Can access
Instance method self Instance and class data
@classmethod cls Class data only
@staticmethod None Neither

Use a classmethod when you need the class, and a staticmethod when you need neither the class nor the instance.

Code example

The snippet builds an object from a string with a classmethod, which is the classic alternative constructor. The staticmethod is a small utility helper that needs no class or instance data.

class Candidate:
    def __init__(self, name, skill):
        self.name = name
        self.skill = skill

    @classmethod
    def from_string(cls, text):
        name, skill = text.split(",")
        return cls(name.strip(), skill.strip())

    @staticmethod
    def is_valid_skill(skill):
        return skill.isalpha()

c = Candidate.from_string("Asha, Python")
print(c.name, c.skill)                   # Asha Python
print(Candidate.is_valid_skill("Java"))  # True

Follow-up questions interviewers ask

Many interview questions about python classes end here, so keep your answers short and name a real use case for each one:

  • Why use a classmethod for constructors? Because cls respects subclasses. If Intern inherits from_string, it returns an Intern, not a Candidate.
  • Can you call both on an instance? Yes. Both also work when called on the class, which is the usual style.
  • Why not write a module-level function? A staticmethod keeps related helper logic grouped with the class it serves.
  • Can a staticmethod change class state? Not without a reference to the class, so reach for a classmethod instead.

15. What is the Global Interpreter Lock (GIL)?

Short answer

One gate letting a single thread through while others queue, beside two separate gates running in parallel.

The GIL is a lock inside CPython that lets only one thread run Python bytecode at a time. It exists because CPython's reference counting is not thread-safe, and one lock is a simple fix. The result is that threads do not speed up CPU-bound work. They still help with I/O-bound work, because a thread releases the lock while it waits on a network call or a file.

Threads help when your program waits, and processes help when your program computes.

Code example

This test runs the same countdown twice, first with two threads and then with two processes. On a multi-core machine, the threaded run takes about as long as running the task twice in a row. The process run is close to twice as fast.

import time
from threading import Thread
from multiprocessing import Pool

def count(n):
    while n > 0:
        n -= 1

if __name__ == "__main__":
    N = 20_000_000

    start = time.perf_counter()
    ts = [Thread(target=count, args=(N,)) for _ in range(2)]
    for t in ts: t.start()
    for t in ts: t.join()
    print("threads:", round(time.perf_counter() - start, 2))

    start = time.perf_counter()
    with Pool(2) as pool:
        pool.map(count, [N, N])
    print("processes:", round(time.perf_counter() - start, 2))

Follow-up questions interviewers ask

This is one of the python language interview questions that separates people who have profiled real code from people who memorized a definition. Be ready for these:

  • How do you get real parallelism? Use multiprocessing or concurrent.futures.ProcessPoolExecutor. Each process has its own interpreter and its own GIL.
  • When are threads still the right choice? For I/O-bound tasks such as API calls, scraping, and database queries.
  • Is the GIL going away? Python 3.13 added an experimental free-threaded build (PEP 703), but the default build still has the GIL.
  • Does the GIL make my code thread-safe? No. Operations like count += 1 can still race, so you need a threading.Lock.

16. How does Python manage memory?

Short answer

CPython stores every object in a private heap that the interpreter manages for you. It frees most memory through reference counting. Each object tracks how many names point to it, and it is freed the moment that count hits zero. Objects that point at each other never reach zero, so a cyclic garbage collector runs now and then to clean up those cycles.

Reference counting frees most objects instantly, and the garbage collector catches the cycles it misses.

Code example

This snippet watches a reference count rise and fall, then builds a reference cycle and lets gc collect it. It is a useful demo when python job interview questions turn to internals.

import sys, gc

a = []
print(sys.getrefcount(a))  # 2, one for a, one for the argument
b = a
print(sys.getrefcount(a))  # 3
del b
print(sys.getrefcount(a))  # 2

class Node:
    pass

x, y = Node(), Node()
x.ref, y.ref = y, x
del x, y
print(gc.collect())  # greater than 0, the cycle was found

Follow-up questions interviewers ask

Expect a practical follow-up about leaks, since this is one of the more advanced interview questions on python programming. Prepare for these:

  • Why does getrefcount show one extra? Passing the object as an argument creates a temporary reference.
  • Does del free memory? No. It removes a name, and the object is freed only when its count reaches zero.
  • Can Python still leak memory? Yes. Unbounded caches, growing global lists, and stray references keep objects alive.
  • What are GC generations? There are three. New objects start in the youngest, and survivors move up and get checked less often.

Final tips for your Python interview

These python job interview questions share one pattern. Interviewers want to see you reason through code, not recite definitions. Run every snippet yourself, change a line, and predict the output before you press enter. That habit builds real fluency faster than rereading answers.

On interview day, explain your thinking out loud and name the trade-offs. A calm "I would check the docs for that" beats a confident wrong answer. Practice the follow-up questions too, because that is where most candidates lose points and where strong candidates stand out.

Finally, if you are hiring instead of interviewing, use this list as a scorecard. Then search verified Python profiles by skill, experience, and location on Olibr and shortlist people who can answer these questions in plain words.

For engineers

Find work worth your time.

Live engineering roles across India and the US, matched to your stack. Build a profile recruiters actually discover.

Browse jobsHow it works

O
§ The author

Olibr Team

Reviewed by Raman Gupta, Founder, Olibr

Filed underHiring Tips
Reading time25 min · 4,816 words

PublishedOctober 3, 2026

CategoryHiring Tips
Enjoyed this piece?Share it with someone who would find it useful.
§ Stay in the loop

Don’t miss the next one.

We publish essays on engineering, hiring, and building teams. Subscribe and we’ll send them when they land.

Unsubscribe anytime · one letter, never more