1. What will print(type(type)) output?
Output: <class 'type'>
This is a question about metaclasses — the classes that classes themselves belong to.
Every object in Python is an instance of some class. An integer is an instance of int; a string is an instance of str. But what is type itself an instance of? The answer is its own metaclass:
typeis the metaclass for all built-in classes —int,str,list, and so on.type(type)asks “what is the class oftype?” The answer:typeis itself an instance oftype. It’s the root of the metaclass hierarchy.
So type(type) prints <class 'type'>.
This is the bootstrapping at the heart of Python’s object model: type is an instance of itself. When you define a class with class Foo:, Python calls type (or a custom metaclass) to create the class object. The interview answer is simply <class 'type'>.
2. What exception is raised when trying to mutate a bytes object?
Answer: TypeError.
bytes is an immutable sequence — like str, its contents cannot be changed after creation. Attempting an item assignment, b[0] = 65, is a mutation, and Python rejects it with a TypeError.
The mutable counterpart is bytearray, which supports in-place modification: ba[0] = 65 works. So the decision between the two mirrors the str vs. list distinction: immutable for safety and hashing, mutable when you need to build up bytes incrementally.
The interview answer: TypeError, because bytes objects are immutable; use bytearray when you need mutable bytes.
3. What does the @staticmethod decorator do?
Answer: It defines a method that receives neither self nor cls — it’s just a plain function living in the class namespace.
The three method kinds form a spectrum:
- Instance method — receives
self, the calling instance. - Class method (
@classmethod) — receivescls, the class. - Static method (
@staticmethod) — receives nothing automatic. It’s a regular function that happens to be defined inside a class, called asMyClass.method(...)orinstance.method(...).
Since it gets no instance and no class, a static method cannot access instance state or class state. It’s used for utility functions that are conceptually related to the class but need no access to it — validation helpers, conversions, factory-adjacent logic that doesn’t need the class.
The interview answer: @staticmethod makes a method that takes no automatic first argument; it behaves like a plain function namespaced inside the class.
4. What is the output of the following code?
a = [1, 2, 3]
b = a[:]
print(a is b)
Output: False
A full slice a[:] creates a new list — a shallow copy. The elements are the same objects, but the list container itself is brand new.
So b is a distinct object from a, and a is b — which tests identity, not content — is False.
The contrast is with plain assignment: b = a would make both names refer to the same list, and a is b would be True. That’s the difference between copying and aliasing.
Note the subtlety: a[:] is a shallow copy. For a flat list of integers that’s a full independent copy. For a list containing lists, the inner lists would still be shared — a deepcopy would be needed for full independence. But for identity, the answer is clean: False, the slice makes a new object.
5. What does itertools.chain([1, 2], [3, 4]) return when iterated?
Answer: A single continuous stream of elements: 1, 2, 3, 4.
itertools.chain takes multiple iterables and concatenates them into one iterator. Iterating it produces the elements of the first iterable, then the elements of the second, and so on — as if they were one long sequence.
It’s lazy: it doesn’t build a combined list up front. It pulls from each input in turn. For the inputs [1, 2] and [3, 4], iterating yields 1, 2, 3, 4 in order. Materializing with list(...) gives [1, 2, 3, 4].
This is the efficient way to iterate over many sequences as one — equivalent to itertools.chain.from_iterable(list_of_iterables) when you have the iterables in a collection. The interview answer: chain yields the elements of all inputs concatenated into one stream.
6. What is the output of print(min("", “a”, “A”, key=len))?
Output: "" (the empty string).
min() with a key function doesn’t compare the elements directly — it compares the result of applying key to each element, and returns the element with the smallest key value.
The key here is len. So the comparison is over lengths:
len("")=0len("a")=1len("A")=1
The smallest length is 0, belonging to the empty string. So min returns "".
The key insight: key=len changes what we minimize over (length, not lexicographic order), but the return value is still the original element, not the key. The interview answer: the empty string, because it has the minimum length.
7. What is the output of print(bool(np.nan)) using standard Python logical evaluation?
Answer: True.
NaN (Not a Number) is a special floating-point value. It has quirky comparison behavior — np.nan == np.nan is False, and every comparison with it is false — which makes people assume it must be falsy. It isn’t.
Truthiness in Python is defined by __bool__, and for floats that’s simply: zero is falsy, everything else is truthy. nan is not zero — it’s a non-zero bit pattern that means “not a number.” So bool(np.nan) is True.
The interview point: NaN being weird for comparisons does not make it falsy. Any non-zero float, including nan, is truthy. Only 0.0 and 0 are falsy numbers.
8. What does str.strip() remove by default?
Answer: Leading and trailing whitespace — spaces, tabs, newlines, and carriage returns — from both ends of the string.
strip() trims from the start and the end of the string, removing whitespace characters until it hits a non-whitespace character on each side. The default whitespace set includes space ( ), tab (\t), newline (\n), carriage return (\r), and a few others.
It does not touch whitespace in the middle of the string, and it doesn’t strip punctuation or other characters unless you pass them as arguments — s.strip(".,!") would strip those specific characters instead.
The related variants: lstrip() strips only the left end, rstrip() only the right. The interview answer: whitespace (space, tab, newline, CR) from the leading and trailing edges.
9. What will print(1, 2, 3, sep=’-’, end=’*’) output?
Output: 1-2-3*
print has two keyword parameters that control formatting:
sep— the separator placed between the positional arguments. Default is a space; here it’s'-', so the values print as1-2-3.end— the string appended after all arguments. Default is a newline; here it’s'*', so instead of a newline, an asterisk follows.
Combined: 1, then -, 2, then -, 3, then * — output 1-2-3* with no trailing newline.
The interview answer: sep='-' joins the values with hyphens and end='*' replaces the newline, giving 1-2-3*.
10. What is the outcome of set([1, 2]) + set([3, 4])?
Answer: TypeError.
Sets do not support the + operator. Addition isn’t defined for them because the natural “add sets together” operations already have dedicated operators:
- Union —
set1 | set2orset1.union(set2)→ all elements from both. - Intersection —
set1 & set2. - Difference —
set1 - set2. - Symmetric difference —
set1 ^ set2.
So set([1, 2]) + set([3, 4]) raises TypeError: unsupported operand type(s) for +: 'set' and 'set'. The interview answer: TypeError — use | or .union() for combining sets.
11. What does the yield from syntax do inside a generator?
Answer: It delegates iteration to a sub-generator or any iterable, yielding all its elements in order.
yield from is a shorthand for “yield everything from this other iterable, one at a time.” For simple cases:
def gen():
yield from [1, 2, 3]
is equivalent to:
def gen():
for x in [1, 2, 3]:
yield x
But yield from is more than a loop, especially for generator delegation. It wires up the full generator protocol: it forwards send(), throw(), and close() from the outer generator to the inner one, and propagates the inner generator’s return value through the yield from expression. This makes it the clean way to compose and reuse generators.
The interview answer: yield from delegates iteration to a sub-generator or iterable, forwarding its elements and the generator control protocol.
12. What does os.path.join(“folder”, “/subfolder”) return on UNIX-like systems?
Answer: "/subfolder".
os.path.join is not a naive string concatenator — it’s path-aware. Its documented behavior: if any component is an absolute path (starts with / on Unix), every previous component is discarded.
"/subfolder" begins with a slash, so it’s absolute. Joining discards "folder" and returns just "/subfolder".
This is a well-known gotcha. You can’t “append” an absolute path onto a prefix and expect nesting — the absolute component resets everything. The safe habit when building paths incrementally is to keep all parts relative and let the last component decide, or strip the leading slash.
The interview answer: an absolute component in os.path.join discards all earlier components, so the result is /subfolder.
13. What is the output of print(3 and 0 or 5)?
Answer: 5
Evaluate left to right, respecting that and binds tighter than or, and that both return operand values.
Step 1: 3 and 0. and returns the first falsy operand, or the last operand if all are truthy. 3 is truthy, so it proceeds to 0, which is falsy → the result is 0.
Step 2: 0 or 5. or returns the first truthy operand. 0 is falsy, so it moves to 5, which is truthy → the result is 5.
So the expression reduces to 5. The interview point is the short-circuit return-value semantics: these operators hand back operands, not booleans, and you must evaluate in stages.
14. Which built-in function returns both the index and value during iteration?
Answer: enumerate().
When you need both the position and the element, enumerate is the tool:
for i, v in enumerate(["a", "b", "c"]):
print(i, v) # 0 a, 1 b, 2 c
It wraps an iterable and yields (index, value) pairs, starting at 0 by default (enumerate(iterable, start=1) starts at 1).
The other options: zip pairs multiple iterables together, map transforms elements, and range just produces a sequence of numbers. Only enumerate gives you index and value together. The interview answer: enumerate().
15. What is the output of the following code?
x = (1)
print(type(x))
Output: <class 'int'>
The parentheses are just grouping — this is the single-element-tuple trap again.
(1) is not a one-element tuple. Without a comma, the parentheses act like arithmetic grouping, and (1) is simply the integer 1. So type(x) is int.
The one-element tuple requires the comma: (1,) is a tuple. The general rule: parentheses make a tuple only when they contain a comma (or when they’re empty, for the empty tuple). x = (1,) would print <class 'tuple'>.
The interview answer: int — (1) is an integer in grouping parentheses, not a tuple.
Premium Content
Unlock Top 50 - Part 2 and all premium lessons with a subscription.
From ₹199.99/year — See plans