Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Top 10 - Part 1
PYTHON

Top 10 - Part 1

Practice 10 of the most important and frequently asked Python programming interview questions.

1. What is the output of the following code?

x = [1, 2, 3]
y = x
y.append(4)
print(x)

Output: [1, 2, 3, 4]

The whole question comes down to one sentence: in Python, variables hold references, not copies.

x = [1, 2, 3] creates a list object in memory and makes x point to it. y = x does not copy the list — it makes y point to the same list object. Both names reference one and the same thing.

Now y.append(4) mutates that shared list. Since x and y are just two names for the same object, the change is visible through both. Printing x shows [1, 2, 3, 4].

If you actually wanted an independent copy, you’d need y = x.copy() (or list(x), or a slice x[:]). Then appending to y would leave x untouched.

The interview point: assignment never copies. It binds a new name to the existing object, and mutable objects are shared through all their names.

2. What is the difference between the == operator and the is operator in Python?

Answer: == compares values; is compares identity (whether two references point to the exact same object in memory).

  • == asks: “do these two objects hold equal values?” It dispatches to the __eq__ method, which can be customized per type. Two distinct lists with the same contents compare equal with ==.
  • is asks: “are these the same object?” It’s reference equality — equivalent to id(a) == id(b). Two objects with identical contents can still fail an is check if they live at different addresses.

The classic gotcha is with small integers and short strings, which Python interns — it reuses the same object for common values. That’s why x = 1000; y = 1000; x is y is often False, while small values like x = 5; y = 5; x is y is True. The interning makes is look like value comparison for some literals, but that’s an implementation detail — is is only for comparing identity.

The practical rules: use is for singleton checks like x is None and x is True; use == for everything that’s actually about values.

3. What is the output of the following slice operation?

nums = [10, 20, 30, 40, 50]
print(nums[::-2])

Output: [50, 30, 10]

A slice is written nums[start:stop:step], and the rules are: start is where you begin (inclusive), stop is where you end (exclusive), and step is how you move. All three can be omitted.

With [::-2], both boundaries are empty, and the step is -2. An empty boundary means “use the natural end of the sequence” — and a negative step means the natural direction is reversed, so the slice starts from the last element.

So it begins at 50, steps backward by 2: 503010. It stops when the negative step runs past the start of the list. The result is [50, 30, 10].

The general trick to remember: [::-1] reverses a sequence, and [::-2] is “every second element, going backward.”

4. What happens when a mutable object is used as a default parameter in a Python function?

def add_item(item, target=[]):
    target.append(item)
    return target

print(add_item(1))
print(add_item(2))

Output: [1] followed by [1, 2]

Default arguments in Python are evaluated exactly once — at function definition time, not on every call. The [] you write in the signature is a single list object, created when the def statement runs, and that same object is reused for every subsequent call that doesn’t pass a target.

So the first call add_item(1) appends to the default list, which now holds [1]. The second call add_item(2) uses the same list — still holding the 1 from before — and appends 2. Output: [1] then [1, 2].

This is the famous mutable-default-argument bug. The state “leaks” between calls, which is almost never what you want.

The standard fix is to default to None and create a fresh list inside the function:

def add_item(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

Now every call that omits target gets a brand-new list. The interview takeaway: defaults are evaluated once; never use a mutable literal as a default.

5. What is the output of the following tuple unpacking statement?

a, *b, c = (1, 2, 3, 4, 5)
print(b)

Output: [2, 3, 4]

This is extended iterable unpacking, also called star unpacking. The * captures “everything in the middle.”

The tuple has five elements: 1, 2, 3, 4, 5. a takes the first (1), c takes the last (5), and the starred variable *b gobbles up everything between them — 2, 3, 4.

One detail that catches people: a starred variable always collects its items into a list, not a tuple. So b is [2, 3, 4], not (2, 3, 4).

The pattern is extremely common — splitting a sequence into a head, a tail, or a middle: first, *rest = data, *head, last = data. The star name can only appear once per unpacking (on the left side of an assignment), and it can be empty when there’s nothing in the middle.

6. Which dictionary method returns a value for a given key if present, or a default value without raising a KeyError?

Answer: dict.get(key, default).

The problem being solved: directly indexing d[key] raises KeyError when the key is missing. get is the safe accessor.

d.get(key) returns the value if the key exists, and None if it doesn’t. With the two-argument form, d.get(key, default) returns default for a missing key. No exception, ever.

The contrast matters for correctness:

d = {"a": 1}
d["b"]          # raises KeyError
d.get("b")      # returns None
d.get("b", 0)   # returns 0

Related but different: setdefault(key, default) inserts the default when the key is absent, then returns the value. And d["b"] = ... is how you write. For reading, get is the tool — the interview answer is dict.get.

7. What will be the output of this code snippet?

print(bool([])), print(bool("False")), print(bool(0.0))

Output: False True False

This tests Python’s truthiness rules — which values are treated as True and which as False in a boolean context.

Three values are being tested:

  • bool([]) — an empty list. All empty collections ([], (), {}, set(), "") are falsy. Result: False.
  • bool("False") — a non-empty string. The contents don’t matter; only whether the string has any characters. "False" has five characters, so it’s truthy. Result: True. This is the one that trips people up — the string saying “False” is still a truthy value.
  • bool(0.0) — numeric zero. Zero of any numeric type (0, 0.0, 0j) is falsy. Result: False.

Output: False True False.

The rule of thumb: None, False, zero, and empty collections are falsy; everything else is truthy.

8. What is the scope resolution order that Python follows for variable lookups?

Answer: LEGB — Local → Enclosing → Global → Built-in.

When Python needs to resolve a name, it searches the scopes in a fixed order:

  1. Local — the current function’s namespace.
  2. Enclosing — the namespaces of outer functions that wrap the current one (for nested functions/closures).
  3. Global — the module level.
  4. Built-in — the builtin namespace (len, print, range, etc.).

The search stops at the first scope that contains the name. If none does, you get a NameError.

A subtle point worth knowing: assignment changes scope. If a function assigns to a variable, that variable is local to the function (unless declared global or nonlocal) — even if an outer scope has the same name. That’s why a line like print(x); x = 1 raises UnboundLocalError when x is later assigned: Python sees the assignment and marks x local for the whole function, so the print finds no local x yet.

The interview answer is simply: LEGB, in that exact order.

9. What will print(type((1))) and print(type((1,))) output respectively?

Answer: int and tuple.

The difference is the trailing comma.

(1) — parentheses with a single value and no comma — are just grouping parentheses, like in arithmetic. (1) is the integer 1 wrapped in brackets that do nothing. So type((1)) is int.

(1,) — the same value with a comma — is the syntax for a one-element tuple. The comma is what makes it a tuple, not the parentheses (the parentheses are optional in most contexts: 1, is also a tuple). So type((1,)) is tuple.

This is a classic interview trap because a single-element tuple has a special syntax that everyone forgets. An empty tuple is () (no comma needed), a one-element tuple is (x,) (comma essential), and two or more elements are (x, y, z).

10. What is the result of evaluating 0.1 + 0.2 == 0.3 in standard Python?

Answer: False.

This isn’t a Python bug — it’s a property of how computers represent floating-point numbers.

Binary floating-point (IEEE 754) can’t represent every decimal exactly. Just as 1/3 has an infinite decimal expansion, 0.1 and 0.2 have infinite binary expansions, and the machine stores a rounded approximation of each. When you add those two approximations, you get something slightly off from 0.3: 0.30000000000000004.

So 0.1 + 0.2 produces 0.30000000000000004, and comparing it to 0.3 fails. The result is False.

The practical rules: never compare floats with ==. Use a tolerance — abs(a - b) < 1e-9 — or, for money and anything needing exact decimal arithmetic, use Python’s decimal.Decimal module. The interview answer: False, because of floating-point rounding.

My Private Notes

Notes are auto-saved locally to this device.