Not the exercise. This counts the digits in a whole number by knocking off the last digit and asking the same question about what is left.
def digits(n):
if n < 10: # one digit left: the answer is 1
return 1
return 1 + digits(n // 10) # this digit, plus however many the rest has
print(digits(7)) # -> 1
print(digits(2026)) # -> 4
- The answer is built on the way back
digits(2026) cannot answer until digits(202) has, which waits for digits(20), which waits for digits(2). The 1 + … in each call is added as the answers come back up.
- Throw the result away and it still runs
Write `digits(n // 10); return 1` and every call still happens, but the answer is always 1. Python has no way to know you meant to use what came back.
- The base case returns a real answer
It is not just a place to stop. digits(7) returning 1 is the value every other answer is built from.
Start a total at 0. For each element: a number is added directly. A list is the same problem, smaller. What does calling nested_sum on it give you?
nested_sum(element) returns that list’s total. Add that to your running total, the same way you add a plain number.
total = 0; for each element, if isinstance(element, list) add nested_sum(element), else add element; return total. An empty list skips the loop and returns 0, which is your base case.
def nested_sum(items): total = 0 for element in items: if isinstance(element, ____): total += ____(element) else: total += ____ return total
def nested_sum(items):
total = 0
for element in items:
if isinstance(element, list):
total += nested_sum(element)
else:
total += element
return total
The inner call does the hard part and hands back one number. The caller only has to add it. Leave out the `total +=` and every level is still visited, but its answer is dropped on the floor.
A list with no inner lists never recurses, and an empty list returns the 0 it started with. There is no need for a separate `if not items`.
total += "2" raises TypeError on its own, which is what the contract asks for. Skipping anything that is not a number would turn a broken input into a wrong total nobody notices.
If the nesting is only ever one level, sum(sum(row) for row in rows) says so and is easier to read. And if you already have flatten, sum(flatten(items)) is two passes but one idea each, which is sometimes worth more than doing it in one.
- Return the total and the count of numbers together, so an average can be taken in one pass.
- Accept tuples as nesting too, and decide what should happen to a string.
- Weight each number by its depth, so a 5 two levels down counts as 10.