Python Arena
Step 2 of 5

Three bugs that hide in functions

Functions that go through a list have a few classic bugs. None of them gives an error message: the function runs and returns a wrong answer, which makes them hard to spot. It pays to know them on sight.

Bug 1: using a variable from outside the function

A function can see the variables of the main program, and using one of them by accident gives a function that only seems to work:

def print_items(items: list):
    for item in shopping:
        print(item)


shopping = ["milk", "eggs"]
print_items(shopping)
print_items(["tea", "rice"])
Output
milk
eggs
milk
eggs

The loop goes through shopping, not the parameter items, so the function prints the same list whatever you give it. It gets worse: in your programs, the lines that try out a function go under if __name__ == "__main__":, and the checks on this site call your function without running that block. Then shopping doesn't exist at all, and the function stops with a NameError. Inside a function, use only its parameters and the variables it creates itself.

Bug 2: overwriting a parameter

This function should count the scores that are at least the given limit:

def count_at_least(scores: list, score: int) -> int:
    count = 0
    for score in scores:
        if score >= score:
            count += 1
    return count


print(count_at_least([3, 8, 5, 10], 6))
Output
4

It should print 2 (for 8 and 10). The loop variable has the same name as the second parameter, so the first round of the loop replaces the limit 6 with the score 3. From then on, score >= score compares a number with itself, which is always true. Give every variable a name of its own:

def count_at_least(scores: list, limit: int) -> int:
    count = 0
    for score in scores:
        if score >= limit:
            count += 1
    return count

The same happens when a helper list inside the function gets the parameter's name, as in scores = []: the list the function was given is gone.

Bug 3: returning too early

return ends the function at once, even in the middle of a loop. This function should tell whether a list has a negative number in it:

def has_negative(numbers: list) -> bool:
    for number in numbers:
        if number < 0:
            return True
        else:
            return False


print(has_negative([4, -2, 7]))
Output
False

The first number, 4, isn't negative, so the else branch returns False before the loop ever reaches -2. One negative number is enough to answer yes, but to answer no, the function has to check every number. So return False belongs after the loop:

def has_negative(numbers: list) -> bool:
    for number in numbers:
        if number < 0:
            return True
    return False


print(has_negative([4, -2, 7]))
print(has_negative([4, 2, 7]))
Output
True
False

Your task

Challenge 1 of 3

all_even(numbers) should return True when every number in the list is even, and False otherwise. It returns too early: all_even([2, 4, 5]) gives True. Fix it.

Challenge 2

What does this program print? Type each line it prints.

Challenge 3

Write a function first_long_word(words, min_length) that returns the first word in the list that has at least min_length characters. If no word is that long, it returns an empty string "".

For example, first_long_word(["sun", "planet", "galaxy"], 5) returns "planet", and first_long_word(["sun", "moon"], 5) returns "".