Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python

Confusing isalpha() results

Hi guys,

I've been experimenting with the isalpha() and remove() from list, and I'm getting some confusing results.

In my experiment, I made a list with alphas and non-alphas and wanted to loop through the list and remove everything that isalpha.

Here's a chunk of code that's been bothering me:

new_list = [1, 2, 3, 4, 'a', 'b', 'c', 'd']

for char in new_list:
    try:
        if char.isalpha():
            print ("Remove {}".format(char))
    except:
        print ("keep {}".format(char))


keep 1
keep 2
keep 3
keep 4
Remove a
Remove b
Remove c
Remove d
 for char in new_list:
    try:
        if char.isalpha():
            new_list.remove(char)
            print ("Remove {}".format(char))
    except:
        print ("keep {}".format(char))


keep 1
keep 2
keep 3
keep 4
Remove a
Remove c
new_list
[1, 2, 3, 4, 'b', 'd']

If the first loop that just prints out the list works, how come the remove() function isn't working as expected?

I've been scratching my head over this one!

Thanks,

Tayler

1 Answer

Anish Walawalkar
Anish Walawalkar
8,534 Points

Hey Tayler, this was definitely a tricky one. I added some extra prints to explain why it skips characters 'b' and 'd'

def main():
    new_list = [1, 2, 3, 4, 'a', 'b', 'c', 'd']

    for index, elem in enumerate(new_list):
        try:
            if elem.isalpha():
                new_list.remove(elem)
                print('Remove {} at index {}'.format(elem, index))
        except:
            print('Keep {} at index {}'.format(elem, index))
        print(new_list)

    print('After for loop')
    print(new_list)


if __name__ == '__main__':
    main()

when you run python code, it prints the following:

Keep 1 at index 0
[1, 2, 3, 4, 'a', 'b', 'c', 'd']
Keep 2 at index 1
[1, 2, 3, 4, 'a', 'b', 'c', 'd']
Keep 3 at index 2
[1, 2, 3, 4, 'a', 'b', 'c', 'd']
Keep 4 at index 3
[1, 2, 3, 4, 'a', 'b', 'c', 'd']
Remove a at index 4
[1, 2, 3, 4, 'b', 'c', 'd']
Remove c at index 5
[1, 2, 3, 4, 'b', 'd']
After for loop
[1, 2, 3, 4, 'b', 'd']

As you can see it runs perfectly until it removes 'a' which is at index 4. Once you remove 'a' from new_list, everything in new_list shifts to the left by 1, therefore 'b' is now at index 4. However the for loop has already gone past index 4 and now moves onto the next index which happens to 5, as a result it misses 'b' and does not remove it from the list. Same thing happens to 'd'. Once it has removed 'c' which is at index 5, it mores everything in new_list to the left by 1. Therefore it misses 'd' which is at index 5 and exits the for loop.

My suggestion is to never modify the original list (new_list) that you are currently looping over. A better approach would be to create an empty list which gets populated in the for loop. In code it looks like this:

def main():
    new_list = [1, 2, 3, 4, 'a', 'b', 'c', 'd']
    updated_list = []
    for index, elem in enumerate(new_list):
        try:
            if elem.isalpha():
                print('Remove {} at index {}'.format(elem, index))
        except:
            print('Keep {} at index {}'.format(elem, index))
            updated_list.append(elem)

    print('After for loop')
    print(updated_list)


if __name__ == '__main__':
    main()

This produces:

Keep 1 at index 0
Keep 2 at index 1
Keep 3 at index 2
Keep 4 at index 3
Remove a at index 4
Remove b at index 5
Remove c at index 6
Remove d at index 7
After for loop
[1, 2, 3, 4]

I hope this explains the abnormal situation you were facing. :)

Ah that makes perfect sense now!

I was confused about how the for loop when though the objects in the list. I thought it would look for the next step rather than follow a strict index.

So when I remove index 4, and it moves onto index 5 that index isn't the same as it used to be because I'd just removed index 4.

Thanks so much for your detailed answer.