Why this assignment
The lessons before this one each covered one idea: writing and calling functions, parameters and return values, default arguments, scope, and the call stack. Real programs use all of them at once, and the skill that interviewers look for is the ability to combine them without losing track. This assignment asks you to build a small receipt program. It has a shop with several items, a per-item total, a discount rule, a check code, and a count of how many lines were printed. Each part uses one of the module's ideas, so completing it shows you can use the whole module, not just the parts you remember best.
Do the work in order. The warm-up section below checks each function separately, and the checklist tells you when the whole program is ready. Do not skip the warm-up, because it is much easier to find a bug in one small function than in a finished program.
You will write one file, main.py, with a function for each job and a main function that runs the program. Type your code into the editor, press Run, and type the example input into the Input box.
The problem in plain words
A shop wants a program that prints a receipt. The first line of input says how many items were bought. Each of the following lines describes one item: its name, its price per unit in whole rupees, and how many units were bought. The program must print one line for each item, then a summary with the subtotal, the discount, the final total, a check code, and the number of item lines printed.
The discount rule is simple. A subtotal of 1000 or more gets 10 percent off, using whole-number division, so 1200 gives a discount of 120. In Python, whole-number division is the // operator. A subtotal below 1000 gets no discount. The check code is the digit sum of the total, which is the sum of its digits. For a total of 1080 the digits are 1, 0, 8 and 0, so the check code is 9. The check code must be computed by a recursive function, and that is one of the requirements.
Here is the format in full. Read it slowly, because every space and every word matters to the output.
Input: the first line holds a whole number n, from 1 to 20. Each of the next n lines holds three values separated by spaces: a name with no spaces, a price from 1 to 10000, and a quantity from 1 to 50.
Output: one line per item, in input order, in the form name qty x price = line, where line is the price times the quantity. After the item lines come five summary lines, in this order: subtotal: S, discount: D, total: T, check: C, and items printed: n.
Examples
Example 1 has three items, none of them discounted:
3
apple 20 4
bread 45 2
milk 60 1The output is:
apple 4 x 20 = 80
bread 2 x 45 = 90
milk 1 x 60 = 60
subtotal: 230
discount: 0
total: 230
check: 5
items printed: 3Example 2 has a subtotal of 1200, so it gets a discount of 120:
2
laptop 900 1
mouse 150 2The output is:
laptop 1 x 900 = 900
mouse 2 x 150 = 300
subtotal: 1200
discount: 120
total: 1080
check: 9
items printed: 2Example 3 is the boundary case. A subtotal of exactly 1000 is discounted, because the rule says 1000 or more:
1
watch 1000 1The output is:
watch 1 x 1000 = 1000
subtotal: 1000
discount: 100
total: 900
check: 9
items printed: 1Check your program against all three examples before you move on. Then think about a case not shown here, such as a single item with quantity 50, and work out the output by hand before you run it.
The functions you must write
Your program must contain these functions, with these names and parameters. The grader reads the code to check the structure as well as the output.
line_total(price, qty=1) returns the price times the quantity. The part =1 is a default value. When a caller leaves out qty, Python uses 1, so line_total(7) returns 7. Python keeps only one function per name, so this one function does the work of two. The lesson on method overloading in this module compares the other tools.
discount_for(subtotal) returns the discount, which is one tenth of the subtotal when it is 1000 or more, and 0 otherwise. Use // for the division.
digit_sum(n) returns the sum of the digits of n. It must be recursive, with a base case for a single digit.
items_printed is a variable outside every function, set to 0 at the top of the file. It goes up by 1 for every item line printed, and main prints it at the end. Python has no static fields, and a variable at the top of a file does the same job. Because main changes the value, main must say global items_printed before it changes it. The common mistakes section shows what happens without that line.
The file also has a main function. It reads the input, calls the three functions above, and prints the output. The last two lines of the reference solution are if __name__ == "__main__": and main(). The first line runs main only when you run this file directly. Copy those two lines as they are.
Warm-up: test each function alone
Work through the warm-up before you write the main program. The first block below is a test program. It calls each function with a value whose answer you can predict. Run it, and check that every line matches the output shown after it.
The output should be:
line_total(4, 3) = 12
line_total(7) = 7
discount_for(999) = 0
discount_for(1000) = 100
digit_sum(1080) = 9Notice that discount_for(1000) sits exactly on the boundary, and its answer 100 shows that the rule means 1000 or more.
The next block tests the recursion on a number you type into the Input box. Type 1080. The check code is the digit sum, so you should see 9. Try a single digit as well. Type 7, and the output should be 7, because the base case returns a single digit unchanged.
def digit_sum(n):
if n < 10:
return n
return n % 10 + digit_sum(n // 10)
n = int(input())
print(f"check: {digit_sum(n)}")With input 1080 the output is check: 9. With input 7 the output is check: 7.
The third warm-up block tests the discount on a subtotal that you type. The total is the subtotal minus the discount, so the program shows both. Type 1200. The discount should be 120 and the total 1080.
def discount_for(subtotal):
if subtotal >= 1000:
return subtotal // 10
return 0
subtotal = int(input())
discount = discount_for(subtotal)
total = subtotal - discount
print(f"discount: {discount}")
print(f"total: {total}")With input 1200 the output is:
discount: 120
total: 1080The fourth block is the starter skeleton. It reads the items and prints the name of each one, so you can see that the input is read in the right order. Use it as the starting point for your program. Replace the line that prints item: with your line-total code, and add the summary lines after the loop. The input is the first example.
items_printed = 0
def main():
global items_printed
n = int(input())
for _ in range(n):
name, price, qty = input().split()
print("item:", name)
items_printed += 1
print(f"items printed: {items_printed}")
if __name__ == "__main__":
main()Type the example 1 input, starting with 3, then the three item lines. The output lists the three names and then items printed: 3.
Some details in the skeleton are new if you have not used Python input before. input() reads one whole line as text. .split() cuts that text at the spaces and gives a list of words. The numbers arrive as text, so int() turns them into whole numbers. The _ in for _ in range(n) is a name that means the loop value is not used. The variables price and qty are read but not used yet. Python does not stop a program for unused variables, so the skeleton runs. When you add the real code, they will be used.
Requirements checklist
Tick each box when you can show it works.
- The file is
main.py, with amainfunction and theif __name__ == "__main__":block. - It reads
nand thennitem lines from the Input box, in order. - It converts the price and the quantity to whole numbers with
int(). - Each item line prints as
name qty x price = line, using the right numbers. - The subtotal is the sum of the item totals, and it is printed after the item lines.
-
discount_forgives 10 percent for a subtotal of 1000 or more, and 0 for less. - The total is the subtotal minus the discount, printed as
total:. -
digit_sumis recursive, has a base case for single digits, and gives the check code. -
line_totalhas a default forqty, so a call with one argument gives the price for one unit. -
items_printedgoes up once per item line,maindeclares it withglobal, and it is printed at the end. - All three examples give exactly the output shown, character for character.
- Each function has a docstring, the short text in triple quotes under the
defline, that says what it does in one sentence.
Common mistakes
Each mistake below comes with the real output from Python 3.14. A traceback, the block of lines Python prints for an error, also shows file names and line numbers. Those lines are omitted here, and the last line names the problem.
Two functions with the same name
def line_total(price):
return price
def line_total(price, qty):
return price * qty
print(line_total(7))Output (last line of the traceback):
TypeError: line_total() missing 1 required positional argument: 'qty'Python does not keep both functions. The second def replaces the first, so the one-argument version is gone. Use one function with a default, as line_total(price, qty=1) does.
Changing items_printed without global
items_printed = 0
def main():
items_printed += 1
main()Output (last line of the traceback):
UnboundLocalError: cannot access local variable 'items_printed' where it is not associated with a valueWhen Python sees items_printed += inside a function, it treats the name as local to that function. The local name has no value yet, so the addition fails. The line global items_printed tells Python to use the name at the top of the file.
Forgetting int()
def line_total(price, qty=1):
return price * qty
price, qty = input().split()
print(line_total(price, qty))Input: 20 4
Output (last line of the traceback):
TypeError: can't multiply sequence by non-int of type 'str'input() always gives text, even when the text looks like a number. Convert each value with int() before you do arithmetic on it.
Using / instead of //
print(2000 / 10)
print(2000 // 10)Output:
200.0
200A single slash gives a decimal result. A discount printed as 200.0 is wrong for this receipt, which uses whole rupees. Use // for whole-number division.
Explaining your design
Beyond the output, you must write three short paragraphs at the top of main.py in a comment. Start each line with #. This is the part that shows understanding, and it is what an interviewer would ask you about.
In the first paragraph, explain why line_total uses a default for qty instead of two functions with the same name. Say what line_total(7) returns, and why the default is a shortcut for a quantity of 1 rather than a second rule. Say what Python does with a second def that has the same name.
In the second paragraph, explain why the names name, price and qty are set inside the loop, but subtotal is set before it. Use the word scope, and say what would go wrong if subtotal = 0 were inside the loop. Python does not give a loop its own scope, so name still exists after the loop. Say what value it would hold then.
In the third paragraph, explain what would happen if you wrote def add_tax(total): total = total + 5 and called it from main with total. Say what the caller sees afterwards, and why. Use the word reference.
Grading rubric
The grader checks the program against the examples, the structure, and the explanation. The table describes what a solution looks like at three levels. Read the 100 percent column first, because it shows the target.
| Area | 60 percent | 80 percent | 100 percent |
|---|---|---|---|
| Output | Gives the right answer for example 1 only | Gives the right answer for all three examples | Gives the right answer for all three examples and for an extra case of your own that you checked by hand |
| Functions | Has one or two of the helpers, with the logic in main | Has all three helpers with the required names and parameters | Has all three helpers, each with one job, and main only reads, calls and prints |
| Default argument | Has line_total, but main multiplies the price and the quantity itself | Has line_total(price, qty=1) and calls it for each item | Has line_total(price, qty=1), calls it for each item, and the explanation says what Python does with two def lines of the same name |
| Recursion | Computes the check code with a loop | Has a recursive digit_sum with a base case | Has a recursive digit_sum with a base case, and the explanation shows the calls for digit_sum(1080) |
| Scope | Some variables are set in the wrong place, and the program works only after fixes | No errors, subtotal is set before the loop, and main declares global items_printed | Each name is set where it is needed, and the explanation names the rule |
| Explanation | Missing or does not match the code | Covers two of the three paragraphs correctly | Covers all three paragraphs, using the words scope and reference correctly |
A 60 percent solution is a program that works on example 1 but does not meet the structure. An 80 percent solution is correct on all examples and has the required functions, but the default or the recursion is weaker than it should be. A 100 percent solution is correct, well-structured, and explained. Aim for 100 percent. The gap between 80 and 100 is almost always the explanation and the edge cases, not the arithmetic.
Hints
The hints are hidden. Open one only when you are stuck, and try the step first.
Hint 1: start with the loop
Show the hint
Read n first, then write a loop that runs n times with for _ in range(n):. Inside the loop, read the name, the price and the quantity in that order with input().split(), and convert the two numbers with int(). Print one line per item before you write any other code, so you can see the input is read correctly.
Hint 2: call line_total with the quantity
Show the hint
Call line_total(price, qty) and store the result in a variable called line, because you will use it twice: once to print and once to add to the subtotal. The default for qty matters only when a call leaves the quantity out.
Hint 3: add to the subtotal in the loop, but set it before
Show the hint
The subtotal must grow across all the items, so it must be set before the loop, to 0. Inside the loop, use subtotal += line to add each item. If you set subtotal = 0 inside the loop, it restarts at 0 on every pass, and the final subtotal is only the last item.
Hint 4: the discount and the total come after the loop
Show the hint
Once the loop ends, you know the final subtotal. Call discount_for(subtotal) to get the discount, and subtract it to get the total. Both values are needed after the loop, so set them after the loop, where they can see the subtotal.
Hint 5: the check code is one call
Show the hint
Call digit_sum(total) inside the f-string you print. Do not compute the digit sum in main with a loop, because the requirement is that the recursive function does the work. If the check code is wrong for a total such as 1080, the bug is in digit_sum, not in main.
Hint 6: the count is a variable at the top of the file
Show the hint
Set items_printed = 0 at the top of the file, outside every function. In main, write global items_printed before you change it, and add 1 each time you print an item line. Print it at the end.
Reference solution
Try the program yourself first. The solution below is the one the grader expects you to match in behaviour, though your code may be laid out differently. Compare it only after you have run your own version against all three examples.
Show the reference solution
# Counts the item lines printed. A variable at the top of the file keeps its value
# between calls, and main uses global to change it.
items_printed = 0
def line_total(price, qty=1):
"""Return the price times the quantity for one line of the receipt.
qty defaults to 1, so line_total(7) returns 7.
"""
return price * qty
def discount_for(subtotal):
"""Return 10 percent off a subtotal of 1000 or more, using whole-number division."""
if subtotal >= 1000:
return subtotal // 10
return 0
def digit_sum(n):
"""Return the sum of the digits of n. The base case is a single digit."""
if n < 10:
return n
return n % 10 + digit_sum(n // 10)
def main():
global items_printed
n = int(input())
subtotal = 0
for _ in range(n):
name, price, qty = input().split()
price = int(price)
qty = int(qty)
line = line_total(price, qty)
subtotal += line
print(f"{name} {qty} x {price} = {line}")
items_printed += 1
discount = discount_for(subtotal)
total = subtotal - discount
print(f"subtotal: {subtotal}")
print(f"discount: {discount}")
print(f"total: {total}")
print(f"check: {digit_sum(total)}")
print(f"items printed: {items_printed}")
if __name__ == "__main__":
main()Each function in the reference has one job, and the summary is computed after the loop.
Test your program with a case that the examples do not cover. A single item with quantity 50 and price 10000 gives a line of 500000, a subtotal of 500000, a discount of 50000, a total of 450000, and a check code of 4 + 5 + 0 + 0 + 0 + 0 = 9. Work this out before you run it, and confirm that your program agrees.
Explaining the design, in the reference
The explanation paragraphs are a required part of the work, and they should be your own words. The reference answers below show the level of detail the grader expects, not a text to copy.
For the default, a good answer says that line_total has one rule, price * qty, and that the default for qty only covers the case where the caller leaves the quantity out. It says that Python has no overloading: a second def with the same name replaces the first, so a default is the tool for this job.
For scope, a good answer says that name, price and qty are needed only within one pass, so they are set in the loop body. It says that subtotal must survive the loop, so it is set before it. If it were set inside, each pass would start from zero, and the subtotal would be only the last item. It also says that a loop does not create a new scope in Python, so name still exists after the loop and holds the last item's name.
For the copy question, a good answer says that add_tax receives the same value that the caller passed. An int cannot change in place. The line total = total + 5 makes the name total inside the function point at a new value, and the caller's name still points at the old one. So main still sees 100. The fix is to return the new value and assign it, as in total = add_tax(total).
Warm-up exercises
Eight short exercises check the module's ideas before you write the assignment. Predict each answer before you look at the answer.
- Write
def square(x):that returnsx * x. Call it with 9. Predict the output before you run it.
Show the answer
square(9) returns 81.
- Write one function
show(value)that printsint:and the value when the value is an int, andtext:and the value otherwise. Call it once with 5 and once with"hi". Python has no overloads, so one function checks the type of its argument.
Show the answer
def show(value):
if isinstance(value, int):
print("int:", value)
else:
print("text:", value)
show(5)
show("hi")The output is int: 5 and then text: hi. The function isinstance checks whether a value has a given type.
- A function
def set_to(v): v = 0is called asx = 5and thenset_to(x). What doesxhold afterwards?
Show the answer
x still holds 5. Inside the function, the name v first points at the value 5. The line v = 0 moves only the name v to a new value. The name x in the caller is unchanged.
- Write
def count_to(n):that counts the passes of a loop from 1 ton, using a local variable calledcount, and returnscount. Then explain whycountdoes not exist after the function returns.
Show the answer
def count_to(n):
count = 0
for i in range(1, n + 1):
count += 1
return countcount_to(4) returns 4. The name count is local to the function. When the function returns, that name is gone. If you then write print(count) at the top level, Python prints NameError: name 'count' is not defined. The end of that message, a suggestion starting with Did you mean, is added by Python and may differ between versions.
- Write
def sum_to(n):recursively, with the base casen == 0returning 0. Call it with 4 and predict the answer.
Show the answer
def sum_to(n):
if n == 0:
return 0
return n + sum_to(n - 1)sum_to(4) gives 10, because 4 + 3 + 2 + 1 + 0 = 10.
- Predict what this prints:
for i in range(4):
pass
print(i)Show the answer
It prints 3. Python does not give a loop its own scope, so i still exists after the loop. Its last value is 3, because range(4) gives 0, 1, 2 and 3. If the loop never runs, the name is never set. For example, for j in range(0): pass followed by print(j) prints NameError: name 'j' is not defined. If you know Java, this is the main difference: Java would refuse to compile the loop variable's use outside the loop.
- Write
def grade(score):that returns"A"for 90 or more and"B"otherwise. Explain which return runs for 95.
Show the answer
def grade(score):
if score >= 90:
return "A"
return "B"For 95, the first if is true, so return "A" runs and the function gives A.
- Describe the call stack for
maincallingf, andfcallingg, at the momentgis running.
Show the answer
From the top: g, f, main. The frame for g is the running one, and the others are waiting for their calls to return. A Python traceback lists the same chain, with the outermost call first and the innermost call last.
What you now know
- A program can be built from small functions, each with one job, and a
mainfunction that reads, calls and prints. - A default argument, such as
qty=1, lets one function handle a call that leaves a value out. - Scope decides where each name can be used. A function body is a scope, and a loop is not. Values needed after a loop are set before it.
- A recursive function needs a base case, and its calls return their answers in reverse order.
- A function gets a reference to each value it is passed, so it changes its caller's data only through a return value, or by changing a shared object such as a list.