• 6. The Most Common Mistakes

 

Back to the Table Of Contents

The Most Common Mistakes

We often get feedback and request to help with the types of errors that new users often get in CheckiO missions. Frequently the code they wrote is working in a local computers dev environment but does not work in the CheckiO editor. Could this be a bug or just some strange mistake in the code? To rule out the a few possibilities, let’s take a look at the most frequent errors new users might experience.

print instead of return

Usually in CheckiO you need to write a function and this function should return a result, not to print it. CheckiO's grader imports a function from your code, runs it with various arguments and checks the returned results.

This code

def checkio(a):
    print(a)
checkio(1)

and this

def checkio(a):
    return a
checkio(1)

are similar if you are using IDLE or an interactive console, but the first function returns "None".

Wrong interpretator

In CheckiO, the default python interpretator is python3 which has some differences from python2. So make sure to check that your local interpretator is the same as in the CheckiO Editor.

For example this code:

array = [1, 2, 3]
print(array[len(array) / 2])

works in python2, but in python3 produces "TypeError: list indices must be integers, not float". This is because in python3 "/" is a real division rather than integer division as in python2.

Iterate and remove

If you try to iterate an array and remove elements from inside a loop, then you will sometimes get interesting results.

For example

def only_even(a):
    for el in a:
        if el % 2:
            a.remove(el)
    return a

print(only_even([1, 2, 3, 4, 5]))
# >>> [2, 4]
print(only_even([1, 3, 4, 6, 5]))
# >>> [3, 4, 6]
# Hm, something wrong

This is because the function can change and reduce an array while iterating it and the loop can "skip" some elements. You can examine this more careful with Pythontutor visualization.

Global variables

In CheckiO, all tests in the same category run in the same environment. As a result, if you are using global variables, running the same function multiple times can cause problems.

For example, in a function which multiplies elements in an array:

ACC = 1

def func(array):
    global ACC
    for el in array:
        ACC *= el
    return ACC

#single run
print(func([1, 2, 3])) # result 6

If you run this script, then you will get a correct result and it looks like your function is correct. Now, if we add function calls to it:

....
#single run
print(func([1, 2, 3]))
# >>> 6
#additional
print(func([1, 2, 3]))
# >>> 72
print(func([10])) 
# >>> 720
# WHAT?!?

The global variable (there are no global constants in python) accumulates data from each of the function calls and after the first run, will give you the wrong results. Be careful with global variables. They can be useful for caching and precalculating data, but can sometimes be dangerous.

Mutable data type as a default argument

It's not obvious but sometimes you will get "weird" errors when you are using an argument with default value such as "[]" or "{}". As with global variables, you will get an error if you try to run your function multiple times.

For example a function to append an element in an array or to create a new array:

def add_or_create(element, array=[]):
    array.append(element)
    return array

print(add_or_create(1))
# >>> [1]
a = [1, 2]
print(add_or_create(2, a))
# >>> [1, 2, 2]

This looks fine, but let's add more calls with using a default argument:

...
print(add_or_create(1))
# >>> [1]
print(add_or_create(2))
# >>> [1, 2]
print(add_or_create(1))
# >>> [1, 2, 1]
# Weird

The default parameter values are always evaluated when, and only when, the “def” statement they belong to is executed. We recommend that you read this article to understand the problem and use a safer construction if you want to use mutable data types as default argument values.

def add_or_create(element, array=None):
    if array is None:
        array = []
    array.append(element)
    return array

print(add_or_create(1))
# >>> [1]
print(add_or_create(1))
# >>> [1]
# Great!

This problem can break your recursion function with an accumulator:

def collect_even(array, acc=[]):
    if not array:
        return acc
    elif array[0] % 2:
        return collect_even(array[1:], acc)
    else:
        acc.append(array[0])
        return collect_even(array[1:], acc)

print(collect_even([1, 2, 3, 4]))
print(collect_even([1, 2, 3, 4]))

Non-ordered data types

Dictionaries and sets are non-ordered data types so if you try to get maximum or minimum complex values when it has several values that can be chosen, then you cannot predict the answer.

For example, we have a dictionary and want a key with the maximum values. If there several "maximum" keys with the same values, then choose a key which is closer to the end of alphabet.

d = {"a": 2, "b": 4, "c": 4, "d": 4, "e": 4, "f": 2}
# wrong attempt
print(max(d.keys(), key=lambda x: d[x]))

Now try to run this script multiple times and you will get varying results. This is because we have several candidates and the dictionary doesn't have an order, so it can return elements in a random order each time. If we want to fix it then:

...
print(max(d.keys(), key=lambda x: (d[x], x)))
40