1. Lighthouse

Level 6 · Debug: the sale changed our list prices

The request

From Thomas Whitaker, Head of Merchandising

“I ran the script that prices the 20% weekend sale, and now our regular price list shows the sale prices too. The "before" and "after" columns are identical. Can you fix it so the list prices stay put?”

Your goal

Fix apply_markdown so it returns a new list of prices reduced by pct percent (each rounded to 2) without changing the list it receives. Set result to a dictionary with two keys: 'list' (the original list_prices, unchanged) and 'sale' (the marked-down prices).

  • Store a dict in resultnot done yet

Your code

Python

Loading the editor…

    ResultPress Run to see yours

    Mote checks your result

    Press Run. Each row that matches the goal becomes a firefly in your lantern.

    The lesson

    Two names, one list

    When you call apply_markdown(list_prices, 20), Python does not copy the list. The parameter prices becomes a second name for the same list object. Anything the function does to prices[i] happens to list_prices too:

    a = [369.00, 159.00]
    b = a            # no copy: two names for one list
    b[0] = 295.20
    a                # [295.2, 159.0]

    Numbers and strings can’t be changed in place, so this never bites with them. Lists and dictionaries can, and that is where the surprise comes from. The starter’s loop with range(len(prices)) walks positions 0 to 4 and writes each sale price back into the caller’s list, which is why the “list” and “sale” columns came out identical. Nothing raised an error; the damage only showed up in the numbers.

    Return a new list instead

    The safe pattern is a function that reads its input and builds a fresh output. A list comprehension does that in one line:

    def apply_markdown(prices, pct):
        return [round(p * (1 - pct / 100), 2) for p in prices]

    After the fix, list_prices still holds 369.00, 159.00, 72.99, 27.99 and 44.99, and the sale prices are 295.20, 127.20, 58.39, 22.39 and 35.99. You can run the function again with 25% and get a correct second answer, because nothing was overwritten the first time.

    If you really need a loop, copy first with prices = list(prices) at the top of the function, then edit the copy. Writing a new list is usually simpler.

    How to spot this kind of bug

    The symptom is always the same: data you didn’t touch changes after a function call. Two quick checks find it. Print the input before and after the call, and look inside the function for assignments into an argument (prices[i] = ..., row["x"] = ...) or methods that change it in place, such as .append(), .sort() or .pop().

    Common mistakes

    • Returning prices.copy() at the end. The original was already changed by the loop.
    • Writing sale_prices = list_prices before the call and thinking it is a copy. It is a second name.
    • Using list_prices.sort() inside a report function, which reorders the caller’s list for good.

    Pick what may run on this site. You can change this any time from "Cookie settings" at the bottom of every page.