Skip to content
Laviyx Learn

Try

Python course menu

Intermediate Python Interview Questions and Answers

Intermediate Python interview questions with explanations and code: mutable default arguments, shallow vs deep copy, *args and **kwargs, generators, late-binding closures, scope, class vs instance attributes, and exceptions.

Intermediate6 min readUpdated
On this page

Intermediate questions test whether you understand how Python behaves, not just its syntax. Many are "predict the output" puzzles built on a single surprising rule. Explain the rule, not just the answer.

1. What does this print? (Mutable default arguments)#

Python
def add(item, items=[]):
    items.append(item)
    return items

print(add(1))
print(add(2))
Show hint

When is the default list created?

Show solution
Output
[1]
[1, 2]

Default values are evaluated once, when the function is defined, so every call shares the same list. Fix: items=None, then if items is None: items = [] inside. See Parameters.

2. Shallow copy vs deep copy?#

Show solution

A shallow copy creates a new outer object but shares the inner objects; a deep copy copies everything recursively:

Python
import copy

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

grid[0][0] = 99
print(shallow[0][0], deep[0][0])
Output
99 1

The shallow copy still points at the same inner lists, so it sees the change.

3. What are *args and **kwargs?#

Show solution

*args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict:

Python
def report(*args, **kwargs):
    print(args, kwargs)

report(1, 2, unit="kg")
Output
(1, 2) {'unit': 'kg'}

They are also used to forward arguments, for example in decorators.

4. List comprehension vs generator expression?#

Show solution

A list comprehension builds the whole list in memory; a generator expression produces values lazily, one at a time, and can be consumed only once:

Python
gen = (n * n for n in range(3))
print(list(gen), list(gen))
Output
[0, 1, 4] []

Use generators for large or one-pass data. See Generators.

5. What does this print? (Late binding in closures)#

Python
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs])
Show hint

When does each lambda look up i?

Show solution
Output
[2, 2, 2]

Closures look up variables when they are called, not when they are created. By then the loop has finished and i is 2. Capture the current value with a default argument:

Python
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs])
Output
[0, 1, 2]

6. Why does this raise UnboundLocalError?#

Python
x = 10
def f():
    print(x)
    x = 5
f()
Show solution

Because x is assigned somewhere in f, Python treats it as a local variable for the whole function. The print(x) then reads the local x before it has a value. Use a different name, pass x as a parameter, or (rarely) declare global x. See Scope.

7. Class attributes vs instance attributes: what is the bug?#

Python
class A:
    def __init__(self):
        self.items = []

class B:
    items = []

a1, a2 = A(), A()
a1.items.append(1)
b1, b2 = B(), B()
b1.items.append(1)
print(a2.items, b2.items)
Show solution
Output
[] [1]

A creates a new list per object in __init__. B.items is a class attribute, shared by every instance, so appending through b1 also changes what b2 sees. Per-object data belongs in __init__. See Classes.

8. What is the difference between sort() and sorted()?#

Show solution
Python
print([1, 2, 3][::-1], sorted([3, 1, 2]), [3, 1, 2].sort())
Output
[3, 2, 1] [1, 2, 3] None

sorted() returns a new list and works on any iterable. list.sort() sorts in place and returns None, so x = x.sort() loses your list.

9. How do try, except, else and finally work together?#

Show solution
  • try: code that might fail.
  • except: runs if a matching exception occurs.
  • else: runs only if no exception occurred.
  • finally: always runs, for clean-up.
Python
try:
    {}["missing"]
except KeyError as e:
    print("caught", repr(e))
finally:
    print("finally always runs")
Output
caught KeyError('missing')
finally always runs

See Exceptions.

10. Which collections module tools do you use?#

Show solution
  • Counter: counts items.
  • defaultdict: a dict with an automatic default for missing keys.
  • deque: fast appends and pops at both ends (queues).
  • namedtuple: lightweight records with named fields.
Python
from collections import Counter, defaultdict

print(Counter("mississippi").most_common(2))

groups = defaultdict(list)
for w in ["apple", "avocado", "banana"]:
    groups[w[0]].append(w)
print(dict(groups))
Output
[('i', 4), ('s', 4)]
{'a': ['apple', 'avocado'], 'b': ['banana']}

Next: Advanced Questions.

Finished this lesson?