Skip to content
RED SAGSkills

Python interview questions and answers

Python interviews mix language fundamentals with practical data handling. You will be asked to predict output, pick the right built-in data structure and explain behaviour like mutability and default arguments.

Below are 60 Python questions with the correct answer and a short explanation, grouped from fresher to experienced level. Try each one before you open the answer.

What Python interviews usually check

  • ✓Lists, dicts, sets and tuples, and when to use each
  • ✓Mutability, references and the mutable default argument trap
  • ✓Comprehensions, generators and iterators
  • ✓Functions, decorators, *args and **kwargs
  • ✓Exceptions, context managers and file handling

Python interview questions for freshers easy

1. Missing key in a Python Counter

With from collections import Counter and c = Counter('apple'), what is c['z']?

  1. A.0
  2. B.KeyError is raised
  3. C.None
  4. D.''
Show answer

Answer: A. 0

Counter returns 0 for keys it has never seen, without adding them, which makes counting code shorter. A plain dict raises KeyError for the same lookup.

2. Creating an empty set in Python

In Python, what type is x after x = {}?

  1. A.set
  2. B.dict
  3. C.list
  4. D.tuple
Show answer

Answer: B. dict

Empty braces create an empty dict, for historical reasons. Use set() for an empty set. {1, 2} with values but no colons does create a set.

3. Removing a missing item from a Python set

What happens with s.remove(9) and s.discard(9) when 9 is not in set s?

  1. A.Both raise KeyError for a missing item
  2. B.Both do nothing for a missing item
  3. C.remove raises KeyError; discard does nothing
  4. D.discard raises KeyError; remove does nothing
Show answer

Answer: C. remove raises KeyError; discard does nothing

remove insists the item is present and raises KeyError otherwise. discard quietly ignores missing items. Pick remove when a missing item would be a bug you want to see.

4. Inverting a Python dict

Which expression turns codes = {'AE': 'UAE', 'IN': 'India'} into {'UAE': 'AE', 'India': 'IN'}?

  1. A.{k: v for v, k in codes}
  2. B.{v: k for k, v in codes.items()}
  3. C.dict(reversed(codes))
  4. D.codes.invert()
Show answer

Answer: B. {v: k for k, v in codes.items()}

A dict comprehension over items() swaps each pair. Iterating codes directly gives only keys, so unpacking fails, and dicts have no invert method. This is safe only when values are unique.

5. What bisect_left returns

In Python, what does bisect.bisect_left([10, 20, 30, 40], 25) return?

import bisect
print(bisect.bisect_left([10, 20, 30, 40], 25))
  1. A.1
  2. B.2
  3. C.3
  4. D.-1
Show answer

Answer: B. 2

bisect_left returns the index where 25 would be inserted to keep the list sorted, which is before 30, at index 2. It never returns -1; for a value larger than everything it returns len(list).

6. Data type for rupee amounts

An invoice table stores amounts in rupees with paise, and GST must add up exactly. Which column type should you use?

  1. A.real
  2. B.numeric(12,2)
  3. C.double precision
  4. D.text
Show answer

Answer: B. numeric(12,2)

numeric stores exact decimal values, so ₹0.10 + ₹0.20 is exactly ₹0.30. Floating-point types like real and double store binary approximations and can produce tiny rounding errors in totals. text would allow invalid values and break arithmetic.

7. Why use with open()

What is the main benefit of writing with open("sales.csv") as f: instead of f = open("sales.csv")?

  1. A.The file is read into memory faster
  2. B.The file is opened in binary mode
  3. C.The file is closed even if an error occurs
  4. D.The file is locked from other programs
Show answer

Answer: C. The file is closed even if an error occurs

A with block uses a context manager, which closes the file when the block ends, whether it ends normally or because of an exception. Without it, an error before f.close() can leave the file open. The same pattern is used for database connections and locks.

8. Renaming one column

Which call renames the column amt to amount and keeps all other columns?

  1. A.df.columns = ["amount"]
  2. B.df.rename(columns={"amt": "amount"})
  3. C.df.rename({"amount": "amt"})
  4. D.df.set_axis(["amount"], axis=1)
Show answer

Answer: B. df.rename(columns={"amt": "amount"})

rename with a columns dict changes only the listed names and returns a new DataFrame. Assigning df.columns or using set_axis replaces every name and fails when the count does not match. In the third option the mapping is backwards and, without columns=, it would apply to the index.

9. Turning a groupby result into a table

totals = df.groupby("city")["sales"].sum() returns a Series with city as the index. How do you get a DataFrame with city and sales columns?

  1. A.totals.to_frame().T
  2. B.totals.reset_index()
  3. C.totals.set_index("city")
  4. D.totals.values
Show answer

Answer: B. totals.reset_index()

reset_index moves the index back into a normal column, giving a two-column DataFrame. Passing as_index=False to groupby gives the same result in one step. Transposing would turn cities into columns, which is not what you want.

10. Labelling orders as high or low

Which line creates a column that says "high" when amount is above 1000 and "low" otherwise?

  1. A.df["band"] = "high" if df["amount"] > 1000 else "low"
  2. B.df["band"] = df["amount"].where("high", "low")
  3. C.df["band"] = np.select(df["amount"] > 1000, "high")
  4. D.df["band"] = np.where(df["amount"] > 1000, "high", "low")
Show answer

Answer: D. df["band"] = np.where(df["amount"] > 1000, "high", "low")

np.where(condition, a, b) works element by element and returns a for True and b for False. A Python if expression needs a single True or False and raises an ambiguous truth value error on a Series. For more than two bands, use np.select with lists of conditions and choices, or pd.cut.

11. Top three products by sales

Which call returns the three rows with the highest sales?

  1. A.df.sort_values("sales").head(3)
  2. B.df["sales"].max(3)
  3. C.df.nlargest(3, "sales")
  4. D.df.top(3, "sales")
Show answer

Answer: C. df.nlargest(3, "sales")

nlargest(n, column) returns the n rows with the largest values, already ordered from highest. sort_values without ascending=False puts the smallest values first, so head(3) gives the bottom three. There is no top method on a DataFrame.

12. Items in one set but not both

Two lists of customer IDs are turned into sets a = {1, 2, 3} and b = {2, 3, 4}. What is a ^ b?

  1. A.{2, 3}
  2. B.{1, 4}
  3. C.{1, 2, 3, 4}
  4. D.{1}
Show answer

Answer: B. {1, 4}

^ is symmetric difference: elements in either set but not in both. Here 1 is only in a and 4 is only in b. & gives the intersection {2, 3}, | gives the union, and a - b gives {1}.

13. Numbering rows from 1

You want to print a ranked list starting at 1. What does this print first?

names = ["Asha", "Omar", "Ravi"]
for i, name in enumerate(names, start=1):
    print(i, name)
  1. A.0 Asha
  2. B.1 Omar
  3. C.1 Asha
  4. D.Asha 1
Show answer

Answer: C. 1 Asha

enumerate yields (index, item) pairs, and start=1 makes the counter begin at 1 instead of 0. The first pair is (1, 'Asha'). This is cleaner than keeping a manual counter or using range(len(names)).

14. Grouping sales by month

A DataFrame has a datetime column order_date and an amount column. Which expression gives total amount per calendar month?

  1. A.df.groupby('order_date')['amount'].sum()
  2. B.df.groupby(df['order_date'].dt.to_period('M'))['amount'].sum()
  3. C.df.groupby(df['order_date'].dt.day)['amount'].sum()
  4. D.df['amount'].sum() / 12
Show answer

Answer: B. df.groupby(df['order_date'].dt.to_period('M'))['amount'].sum()

dt.to_period('M') turns each date into a monthly period like 2026-03, so all days of a month group together. Grouping by the raw date gives daily totals, and dt.day would mix the 5th of every month. resample('MS') on a date index is another option.

15. f-string with an equals sign

What does this print in Python 3.8 or later?

total = 2500
print(f"{total=}")
  1. A.2500
  2. B.{total=}
  3. C.total=2500
  4. D.SyntaxError
Show answer

Answer: C. total=2500

Adding = inside an f-string field prints the expression text, an equals sign and its value. It is a quick way to debug values without typing the name twice. You can combine it with format specs, as in f"{total=:,}".

16. Lost leading zeros in IDs

After importing a CSV into Excel, employee IDs like 00451 now show as 451. What is the best fix?

  1. A.Import the ID column as Text so it keeps its exact characters
  2. B.Multiply the column by 1000
  3. C.Format the cells as Currency
  4. D.Sort the column ascending
Show answer

Answer: A. Import the ID column as Text so it keeps its exact characters

Excel guessed the column was a number and dropped the leading zeros. IDs, phone numbers and postal codes are labels, not quantities, so they should be stored as text. Set the column type to Text during import (for example in Power Query) so the original value is kept.

17. Type of a 1 to 5 rating

A survey asks customers to rate satisfaction from 1 (very unhappy) to 5 (very happy). What type of variable is this?

  1. A.Continuous
  2. B.Nominal
  3. C.Ratio
  4. D.Ordinal
Show answer

Answer: D. Ordinal

The values have a clear order, but the gap between 1 and 2 is not guaranteed to equal the gap between 4 and 5. That makes it ordinal. Medians and percentage of 4s and 5s are often safer summaries than a precise mean.

18. Box plots per city in Python

A data scientist has a pandas DataFrame df with columns city and delivery_min. Which seaborn call draws one box plot per city?

  1. A.sns.lineplot(data=df, x="delivery_min")
  2. B.sns.heatmap(df["city"])
  3. C.sns.countplot(data=df, x="delivery_min")
  4. D.sns.boxplot(data=df, x="city", y="delivery_min")
Show answer

Answer: D. sns.boxplot(data=df, x="city", y="delivery_min")

sns.boxplot with a categorical x and numeric y draws one box per category. countplot counts rows per value, lineplot draws lines, and heatmap needs a 2D matrix of numbers.

19. Cleaning extra spaces from names

Names imported from a form look like " Anil Sharma ". Which function turns this into "Anil Sharma"?

  1. A.CLEAN
  2. B.PROPER
  3. C.TRIM
  4. D.SUBSTITUTE only
Show answer

Answer: C. TRIM

TRIM removes spaces at the start and end, and reduces runs of spaces between words to one. CLEAN removes non-printing control characters, and PROPER changes capitalisation. Extra spaces are a very common reason lookups fail.

20. Deleting duplicate rows

A staging table has repeated rows for the same order_id. Which pattern keeps one row per order_id?

  1. A.Number rows per order_id with ROW_NUMBER and keep rn = 1
  2. B.Rank rows over the whole table with RANK and keep rank = 1
  3. C.Add COUNT(*) per order_id and keep rows where it is 1
  4. D.Use LAG(order_id) and keep rows where it is NULL
Show answer

Answer: A. Number rows per order_id with ROW_NUMBER and keep rn = 1

ROW_NUMBER over PARTITION BY order_id numbers each copy 1, 2, 3, so keeping rn = 1 leaves one row per order. Keeping COUNT = 1 throws away every duplicated order entirely. Order the window by a load timestamp so you keep the copy you want.

Python intermediate interview questions medium

21. Using a list as a dict key in Python

In Python, seen[[r, c]] = True raises TypeError: unhashable type: 'list'. What is the usual fix?

  1. A.Use seen[str([r, c])] only for lists of length 2
  2. B.Convert the dict to a set
  3. C.Use a tuple: seen[(r, c)] = True
  4. D.Sort the list before using it
Show answer

Answer: C. Use a tuple: seen[(r, c)] = True

Lists are mutable, so Python does not let them be hashed. Tuples of hashable values are immutable and hashable, which makes them the normal choice for grid coordinates and composite keys.

22. Subtracting two Counters

What is Counter(a=3, b=1) - Counter(a=1, b=2) in Python?

  1. A.Counter({'a': 2, 'b': -1})
  2. B.Counter({'a': 2})
  3. C.Counter({'a': 2, 'b': 0})
  4. D.Counter({'a': 4, 'b': 3})
Show answer

Answer: B. Counter({'a': 2})

The - operator keeps only positive results, so b (1 - 2 = -1) is dropped. If you need negatives, use c.subtract(other), which updates in place and keeps zero and negative counts.

23. A script that hangs forever

An automation script sometimes hangs for hours on this line. What is the fix?

resp = requests.get("https://inventory.internal/api/hosts")
  1. A.Wrap the call in a try/except for ValueError
  2. B.Run the script with sudo
  3. C.Pass a timeout, for example requests.get(url, timeout=(3, 10)), and handle the timeout exception
  4. D.Switch the URL from https to http
Show answer

Answer: C. Pass a timeout, for example requests.get(url, timeout=(3, 10)), and handle the timeout exception

The Python requests library has no default timeout, so a server that accepts the connection but never answers can block forever. Passing a connect and read timeout makes the call fail fast, and then the script can retry with backoff or alert. Every network call in automation should have a timeout.

24. Changing int to bigint

You run ALTER TABLE orders ALTER COLUMN id TYPE bigint on a 500 GB table. What should you expect?

  1. A.An instant catalog-only change
  2. B.A change only for new rows
  3. C.A full rewrite while all access is blocked
  4. D.A rewrite that runs online without locks
Show answer

Answer: C. A full rewrite while all access is blocked

int and bigint have different on-disk sizes, so every row and every index containing the column must be rewritten, and an ACCESS EXCLUSIVE lock blocks all access the whole time. On large tables teams add a new bigint column, backfill in batches, then swap in a short window.

25. Copying a list of lists

What does print(a) show?

a = [[1, 2], [3]]
b = a.copy()
b[0].append(9)
b.append([4])
print(a)
  1. A.[[1, 2, 9], [3]]
  2. B.[[1, 2], [3]]
  3. C.[[1, 2, 9], [3], [4]]
  4. D.[[1, 2], [3], [4]]
Show answer

Answer: A. [[1, 2, 9], [3]]

list.copy() is a shallow copy: b is a new outer list, but its items are the same inner list objects as in a. Appending to b[0] changes the inner list that a also holds, while appending a new item to b does not touch a. Use copy.deepcopy when you need independent nested data.

26. Sorting by score, then by name

You want students sorted by score from highest to lowest, and alphabetically by name when scores tie. Which call does it?

  1. A.sorted(rows, key=lambda r: (-r["score"], r["name"]))
  2. B.sorted(rows, key=lambda r: (r["score"], r["name"]), reverse=True)
  3. C.sorted(rows, key=lambda r: r["score"] - r["name"])
  4. D.sorted(rows, key=lambda r: [r["name"], -r["score"]])
Show answer

Answer: A. sorted(rows, key=lambda r: (-r["score"], r["name"]))

A tuple key sorts by the first item, then uses the second to break ties. Negating the numeric score gives descending order while names stay ascending. Using reverse=True would reverse both parts, putting names in Z to A order on ties.

27. What *args and **kwargs receive

What does this print?

def f(a, *args, **kwargs):
    print(a, args, kwargs)

f(1, 2, 3, city="Pune")
  1. A.1 [2, 3] {'city': 'Pune'}
  2. B.1 (2, 3) {'city': 'Pune'}
  3. C.(1, 2, 3) () {'city': 'Pune'}
  4. D.1 (2, 3, 'Pune') {}
Show answer

Answer: B. 1 (2, 3) {'city': 'Pune'}

The first positional argument fills a, extra positional arguments are collected into the tuple args, and keyword arguments not matched by name go into the dict kwargs. So args is (2, 3), a tuple not a list, and kwargs is {'city': 'Pune'}.

28. Inverting a dict with repeated values

What is the result of this comprehension?

d = {"a": 1, "b": 2, "c": 1}
{v: k for k, v in d.items()}
  1. A.{1: 'c', 2: 'b'}
  2. B.{1: 'a', 2: 'b'}
  3. C.{1: ['a', 'c'], 2: ['b']}
  4. D.It raises a KeyError for the duplicate
Show answer

Answer: A. {1: 'c', 2: 'b'}

Keys in a dict are unique, so when the value 1 appears twice, the later pair overwrites the earlier one. Items are visited in insertion order, so "c" wins over "a". If you need all keys per value, build a dict of lists with defaultdict(list).

29. Named columns from groupby

Which call returns one row per city with columns called total and orders?

  1. A.df.groupby("city").agg(total=("sales", "sum"), orders=("order_id", "nunique"))
  2. B.df.groupby("city").agg({"total": "sum", "orders": "nunique"})
  3. C.df.groupby("city").sum(total="sales", orders="order_id")
  4. D.df.groupby("city")[["sales", "order_id"]].agg(["total", "orders"])
Show answer

Answer: A. df.groupby("city").agg(total=("sales", "sum"), orders=("order_id", "nunique"))

Named aggregation takes keyword arguments where each value is a (column, function) pair, and the keyword becomes the output column name. The dict form expects existing column names as keys, so "total" would fail. Named aggregation avoids messy multi-level column names.

30. A 7-day window on irregular dates

Sales are recorded on Jan 1 (1), Jan 3 (2) and Jan 9 (3), with the date as the index. What does rolling("7D").sum() give on Jan 9?

  1. A.6
  2. B.5
  3. C.3
  4. D.NaN
Show answer

Answer: B. 5

A "7D" window covers the 7 days ending at each row, here Jan 3 to Jan 9, so it includes 2 and 3. Jan 1 is more than 7 days back and drops out. rolling(3) would count rows instead of days and give 6, which is wrong when dates have gaps.

31. Showing UTC timestamps in Dubai time

event_time holds UTC times with no timezone attached. Which .dt steps convert it to Dubai local time?

  1. A.tz_localize("UTC"), then tz_convert("Asia/Dubai")
  2. B.tz_localize("Asia/Dubai") on its own
  3. C.tz_convert("Asia/Dubai") on its own
  4. D.Subtract 4 hours with pd.Timedelta(hours=4)
Show answer

Answer: A. tz_localize("UTC"), then tz_convert("Asia/Dubai")

tz_localize says what timezone naive times are already in, and tz_convert moves aware times to another zone. Localising straight to Asia/Dubai would label UTC times as Dubai times without shifting them. tz_convert on naive times raises an error, and Dubai is UTC+4, so subtracting 4 hours goes the wrong way.

32. Columns called price_x and price_y

After orders.merge(products, on="sku"), the result has price_x and price_y. What does that mean?

  1. A.The merge found two prices for each sku
  2. B.price was used as a second merge key
  3. C.The merge failed for some rows
  4. D.Both tables had a non-key column called price
Show answer

Answer: D. Both tables had a non-key column called price

When both sides share a column name that is not a key, pandas keeps both and adds _x (left) and _y (right). Use suffixes=("_order", "_list") to give clear names, or drop or rename the column on one side first. Here it is likely the sale price versus the list price.

33. Ranking scores with ties

Scores are 100, 90, 90 and 80. With ascending=False, what ranks does method="dense" give?

  1. A.1, 2, 2, 4
  2. B.1, 2, 3, 4
  3. C.1, 2.5, 2.5, 4
  4. D.1, 2, 2, 3
Show answer

Answer: D. 1, 2, 2, 3

Dense ranking gives ties the same rank and the next score the next whole number, so 80 is 3. method="min" also gives ties rank 2 but skips to 4 for the next score, like sports rankings. The default method="average" gives each tie 2.5.

34. Building a long string in a loop

A loop builds a CSV line for each of 1 million rows and adds each to a big string with +=. What is the most reliable way to make this fast?

  1. A.Use a while loop instead of a for loop
  2. B.Convert each line to bytes before adding
  3. C.Add lines in reverse order
  4. D.Collect the lines in a list and call ''.join(lines)
Show answer

Answer: D. Collect the lines in a list and call ''.join(lines)

Strings are immutable, so += may copy the whole string each time, which can turn the loop quadratic. Collecting pieces in a list and joining once does a single pass. For files, writing lines directly or using the csv module avoids the big string entirely.

35. Sorting words by length

What does this print?

words = ["bb", "a", "cc", "d"]
print(sorted(words, key=len))
  1. A.['a', 'bb', 'cc', 'd']
  2. B.['a', 'd', 'bb', 'cc']
  3. C.['d', 'a', 'cc', 'bb']
  4. D.['a', 'd', 'cc', 'bb']
Show answer

Answer: B. ['a', 'd', 'bb', 'cc']

Python's sort is stable: items with equal keys keep their original order. 'a' and 'd' both have length 1 and 'a' came first; 'bb' came before 'cc'. Stability is what lets you sort by a second key first, then by the main key.

36. Outlier cutoff with the IQR rule

For order values, Q1 = 10 and Q3 = 20. Using the 1.5 x IQR rule, above what value is an order flagged as a high outlier?

  1. A.30
  2. B.25
  3. C.35
  4. D.40
Show answer

Answer: C. 35

The interquartile range is Q3 - Q1 = 10. The upper fence is Q3 + 1.5 x IQR = 20 + 15 = 35, and the lower fence is 10 - 15 = -5. The IQR rule uses quartiles, so it is not pulled around by the outliers themselves the way a mean plus standard deviation rule is.

37. LAG in pandas

In pandas, which line matches LAG(sales) OVER (PARTITION BY store ORDER BY date)?

  1. A.df.sort_values('date').groupby('store')['sales'].shift(-1)
  2. B.df.sort_values('date')['sales'].shift(1)
  3. C.df.sort_values('date').groupby('store')['sales'].cumsum()
  4. D.df.sort_values('date').groupby('store')['sales'].shift(1)
Show answer

Answer: D. df.sort_values('date').groupby('store')['sales'].shift(1)

shift(1) moves values down one row, like LAG, and groupby keeps each store separate. You must sort by date first or the "previous" row is arbitrary. shift(-1) is LEAD, and without groupby the shift crosses stores.

38. Is 03/04/2026 March or April

A partner file has dates like 03/04/2026, and some rows were loaded as 4 March while others as 3 April. What is the best fix?

  1. A.Let the loader guess the format for each row
  2. B.Parse one agreed format; prefer ISO 8601
  3. C.Store the dates as text forever
  4. D.Drop rows where the day is 12 or less
Show answer

Answer: B. Parse one agreed format; prefer ISO 8601

dd/MM and MM/dd look the same for days 1–12, so automatic inference mixes them. Parse with the format the partner confirms, and prefer ISO 8601 (yyyy-MM-dd) in contracts because it is unambiguous and sorts correctly.

39. Dates without a time zone

An API returns "deliveryAt": "2026-05-10 18:00:00" with no offset. Clients in India and the UAE show different times. What should the contract require?

  1. A.The date in DD/MM/YYYY format only
  2. B.The time written in words, e.g. "six pm"
  3. C.The date as the number of days since 2000
  4. D.ISO 8601 with an offset, e.g. ...T18:00:00+04:00
Show answer

Answer: D. ISO 8601 with an offset, e.g. ...T18:00:00+04:00

A time with no zone is ambiguous; each client guesses its own zone. ISO 8601 with Z or an explicit offset makes the instant clear. Tests should check the format and that the value is right for the business zone.

40. DSO bug on quarterly data

Each row of df holds one quarter: quarter-end receivables and that quarter's revenue. What is wrong with this DSO calculation?

df['dso'] = df['receivables'] / df['revenue'] * 365
  1. A.It should divide revenue by receivables
  2. B.It must use average revenue of all rows
  3. C.Nothing; the formula is correct
  4. D.It uses 365 days with one quarter's revenue
Show answer

Answer: D. It uses 365 days with one quarter's revenue

Quarterly revenue covers about 91 days, so multiplying by 365 overstates DSO roughly fourfold. Use the days in the quarter (about 91), or annualise revenue first.

Python interview questions for experienced candidates hard

41. Is Python dict order guaranteed

Which statement about iteration order of Python dicts and sets is correct today?

  1. A.Dicts keep insertion order by spec; sets do not
  2. B.Both dicts and sets keep insertion order
  3. C.Neither has a defined order
  4. D.Dicts are sorted by key; sets by value
Show answer

Answer: A. Dicts keep insertion order by spec; sets do not

Since Python 3.7 the language guarantees that dicts preserve insertion order. Sets make no ordering promise, and string hash randomization can change their order between runs. Use a dict with None values, or sort, when order matters.

42. Set order changing between runs

A Python script prints list(set(names)) and the order differs between runs, breaking a snapshot test. Why?

  1. A.String hashes are randomized per process
  2. B.Sets are re-sorted by the garbage collector
  3. C.Python sets use insertion order but drop it on copy
  4. D.list() sorts the set randomly
Show answer

Answer: A. String hashes are randomized per process

Python seeds string hashing randomly at startup (PYTHONHASHSEED) to resist hash flooding, so set iteration order for strings changes between runs. Sort the output, or use dict.fromkeys to keep insertion order.

43. Python keys 1, 1.0 and True

What is {1: 'a', 1.0: 'b', True: 'c'} in Python?

  1. A.{1: 'a', 1.0: 'b', True: 'c'}
  2. B.{True: 'c'}
  3. C.{1: 'c'}
  4. D.{1: 'a'}
Show answer

Answer: C. {1: 'c'}

1 == 1.0 == True and they have the same hash, so they are one key. The first inserted key object, 1, is kept, while each later assignment replaces the value, so the final value is 'c'.

44. Rolling mean at the edges

What does this code print?

import pandas as pd

s = pd.Series([10, 20, 30, 40, 50])
print(s.rolling(3).mean().iloc[[1, 4]].tolist())
  1. A.[15.0, 40.0]
  2. B.[nan, 40.0]
  3. C.[20.0, 40.0]
  4. D.[nan, 45.0]
Show answer

Answer: B. [nan, 40.0]

rolling(3) needs three values by default (min_periods equals the window), so position 1 has only two and returns NaN. Position 4 averages 30, 40 and 50 to give 40.0. The window is trailing, which is right for features, since it never looks ahead.

45. Reading a global before assigning it

What happens when show() is called?

count = 10
def show():
    print(count)
    count = 5
show()
  1. A.It prints 10
  2. B.It prints 5
  3. C.It raises UnboundLocalError
  4. D.It prints 10 and changes the global to 5
Show answer

Answer: C. It raises UnboundLocalError

Because count is assigned somewhere inside show, Python treats count as a local variable for the whole function. The print runs before the local has a value, so it raises UnboundLocalError. Declare global count if you really mean the global, or better, pass and return values.

46. A list default in a dataclass

What happens when Python reaches this class definition?

from dataclasses import dataclass

@dataclass
class Cart:
    items: list = []
  1. A.Every Cart shares the same items list
  2. B.Every Cart gets its own empty list
  3. C.It works, but mypy shows a warning
  4. D.It raises a ValueError right away
Show answer

Answer: D. It raises a ValueError right away

dataclasses refuse list, dict and set defaults and raise ValueError when the class is defined, because one shared list would leak between instances. The fix is items: list = field(default_factory=list), which calls list() for each new object. This is the dataclass version of the mutable default argument trap.

47. round() on .5 values

What does print(round(2.5), round(3.5)) show?

  1. A.3 4
  2. B.2 3
  3. C.2 4
  4. D.3 3
Show answer

Answer: C. 2 4

Python's round uses "round half to even", also called banker's rounding: ties go to the nearest even number, so 2.5 becomes 2 and 3.5 becomes 4. This avoids an upward bias when rounding many values. For invoice rounding that must always go half up, use decimal.Decimal with ROUND_HALF_UP.

48. nunique and unique disagree

What are s.nunique() and len(s.unique())?

s = pd.Series([1, 1, np.nan, 2, np.nan])
  1. A.2 and 2
  2. B.2 and 3
  3. C.3 and 3
  4. D.3 and 2
Show answer

Answer: B. 2 and 3

nunique() skips missing values by default, so it counts only 1 and 2. unique() returns every distinct value including NaN once, giving [1, 2, nan], which has length 3. Use nunique(dropna=False) if NaN should count as its own value.

49. Row count after explode

How many rows does df.explode("tags") return?

df = pd.DataFrame({"id": [1, 2, 3],
                   "tags": [["a", "b"], ["c"], []]})
  1. A.3
  2. B.2
  3. C.4
  4. D.5
Show answer

Answer: C. 4

explode makes one row per list item, so id 1 gives two rows and id 2 gives one. An empty list does not disappear; it becomes a single row with NaN in tags. That gives 4 rows, and the repeated index labels show which original row each came from.

50. Day-over-day change for many stores

df holds daily sales for many stores, sorted by store then date. df["sales"].pct_change() gives odd values on the first day of each store. Why, and what is the fix?

  1. A.pct_change needs a DatetimeIndex; set date as the index
  2. B.The first value is always wrong; drop it with iloc
  3. C.It needs fill_method=None to stop forward filling
  4. D.It crosses store boundaries; use groupby("store") first
Show answer

Answer: D. It crosses store boundaries; use groupby("store") first

pct_change compares each row with the row above, ignoring which store it belongs to. At each store boundary, it divides by the previous store's last value. Grouping by store restarts the calculation per store, giving NaN on each store's first day, which is correct.

51. Views and copies in NumPy

What does print(a) show?

a = np.arange(5)
b = a[1:3]
b[0] = 99
c = a[[3, 4]]
c[0] = -1
print(a)
  1. A.[ 0 1 2 3 4]
  2. B.[ 0 99 2 -1 4]
  3. C.[ 0 99 2 3 4]
  4. D.[ 0 1 2 -1 4]
Show answer

Answer: C. [ 0 99 2 3 4]

Basic slicing returns a view that shares memory with a, so writing to b changes a. Fancy indexing with a list of positions returns a new copy, so writing to c leaves a alone. Call .copy() on a slice when you need independent data.

52. Merging an int key with a text key

orders.cust_id is int64, but customers.cust_id was read as text like "101". What does orders.merge(customers, on="cust_id") do?

  1. A.It matches the keys by converting both to text
  2. B.It returns an empty DataFrame
  3. C.It raises a ValueError about int64 and str columns
  4. D.It returns all rows with NaN customer columns
Show answer

Answer: C. It raises a ValueError about int64 and str columns

pandas refuses to merge numeric keys with string keys and raises a ValueError telling you the types differ. Fix the types first, for example customers["cust_id"] = customers["cust_id"].astype("int64"), after stripping spaces. Checking df.dtypes before a merge saves a lot of debugging.

53. Different standard deviations for the same data

For x = [2, 4, 4, 4, 5, 5, 7, 9], np.std(x) gives 2.0 but pd.Series(x).std() gives about 2.14. Why?

  1. A.pandas skips the first value
  2. B.NumPy divides by n, pandas divides by n - 1
  3. C.NumPy rounds the result to one decimal
  4. D.pandas uses the median instead of the mean
Show answer

Answer: B. NumPy divides by n, pandas divides by n - 1

The squared deviations sum to 32. NumPy's default ddof=0 gives the population formula, sqrt(32 / 8) = 2.0, while pandas defaults to ddof=1, the sample formula, sqrt(32 / 7) which is about 2.14. Pass ddof explicitly so results match across tools.

54. groupby().first() mixes rows

Group "a" has rows (p=NaN, q=1) and (p="x", q=2). What does df.groupby("c").first() return for group "a"?

  1. A.p = NaN and q = 1
  2. B.p = "x" and q = 2
  3. C.p = NaN and q = 2
  4. D.p = "x" and q = 1
Show answer

Answer: D. p = "x" and q = 1

first() returns the first non-missing value of each column separately, so p comes from the second row and q from the first. The output row never existed in the data. Use .head(1) or .nth(0) when you need the actual first row.

55. Dividing by zero in pandas

What does pd.Series([10, 0]) / pd.Series([0, 0]) return?

  1. A.A ZeroDivisionError
  2. B.inf and NaN
  3. C.0 and 0
  4. D.NaN and NaN
Show answer

Answer: B. inf and NaN

pandas follows floating-point rules: a positive number divided by 0 gives inf, and 0 / 0 gives NaN, with no error. Infinite values then wreck averages and charts. Replace zero denominators first, or clean the result with .replace([np.inf, -np.inf], np.nan).

56. Where NaN goes in a descending sort

A score column holds [70, NaN, 90, 80]. Where does NaN end up after df.sort_values('score', ascending=False)?

  1. A.First, since NaN counts as the largest
  2. B.Last, because na_position defaults to 'last'
  3. C.It is dropped from the result
  4. D.Between 80 and 70
Show answer

Answer: B. Last, because na_position defaults to 'last'

pandas puts missing values at the end by default whatever the sort direction, because na_position='last'. The result is 90, 80, 70, NaN. Pass na_position='first' to put them at the top, or dropna() before ranking.

57. Times stored as text

An attendance export has times as text like "09:45". =B2-A2 works for some rows but others give #VALUE! because they hold "9.45 am". Which approach is most reliable?

  1. A.Format the column as Time and retype nothing
  2. B.Multiply every cell by 24 to convert it into hours first
  3. C.Use TODAY() to give each time a date
  4. D.Standardise the text, then convert with TIMEVALUE
Show answer

Answer: D. Standardise the text, then convert with TIMEVALUE

Excel can convert some text times on the fly, but not non-standard ones like "9.45 am". Clean them first, for example SUBSTITUTE the dot with a colon, then use TIMEVALUE to get a real time. Formatting alone never changes text into numbers.

58. Last known value with IGNORE NULLS

In Snowflake, which expression fills each NULL reading with the latest earlier non-NULL reading?

  1. A.FIRST_VALUE(reading) IGNORE NULLS OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING)
  2. B.LAG(reading) OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING)
  3. C.LAST_VALUE(reading) IGNORE NULLS OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING)
  4. D.LAST_VALUE(reading) OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING)
Show answer

Answer: C. LAST_VALUE(reading) IGNORE NULLS OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING)

With a frame from the start to the current row, LAST_VALUE IGNORE NULLS returns the most recent non-NULL value. FIRST_VALUE IGNORE NULLS returns the very first reading in the series. Without IGNORE NULLS, LAST_VALUE just returns the current row, NULL included.

59. Template lost inside an f-string

A templated sql field is built as below. The query fails because the date filter is wrong. What did the operator actually receive?

table = "sales"
sql = f"SELECT * FROM {table} WHERE dt = '{{ ds }}'"
  1. A.SELECT * FROM sales WHERE dt = '{ ds }'
  2. B.SELECT * FROM sales WHERE dt = '2026-04-02'
  3. C.SELECT * FROM {table} WHERE dt = '{{ ds }}'
  4. D.SELECT * FROM sales WHERE dt = ''
Show answer

Answer: A. SELECT * FROM sales WHERE dt = '{ ds }'

In a Python f-string, {{ and }} are escapes for literal single braces, so the string becomes { ds }, which Jinja does not treat as a template. Write {{{{ ds }}}} inside the f-string, or build the SQL with normal string joining.

60. Sending money amounts in JSON

An API sends "amount": 1999.99 as a JSON number. A tester suggests 199999 in paise, or the string "1999.99". Why?

  1. A.JSON does not allow decimal numbers at all
  2. B.Strings are always faster to parse than numbers
  3. C.Binary floats cannot hold many decimals exactly
  4. D.Paise values are needed only for GST rules
Show answer

Answer: C. Binary floats cannot hold many decimals exactly

Many JSON parsers turn decimals into binary floats, which can add tiny errors that grow in sums. Integer minor units (paise, fils) or decimal strings keep money exact. Tests should check the type and value match the contract.

Ready to test yourself?

Take a 15-question Python MCQ test with a score at the end, or practise the full Python question bank and earn points on the leaderboard.