3

My instructions in a codecademy project are to

Define a function called purify that takes in a list of numbers, removes all odd numbers in the list, and returns the result. For example, purify([1,2,3]) should return [2]. Do not directly modify the list you are given as input; instead, return a new list with only the even numbers.

The code I have come up with is:

def purify(numbers):

pure = numbers

for num in pure:
    if num % 2 != 0:
        pure = pure.remove(num)

return pure

And the error is:

Oops, try again. Your function crashed on [1] as input because your function throws a "'NoneType' object is not iterable" error.

As I've come to understand it, this means something in my code is being returned as "None". Or there is no data there. I can't seem to find what's wrong with this short and simple code, unless it's in my if statement. Thanks in advance.

So I made the edit within the code to remove "pure = pure.remove(num)" and that solved the none issue. However, when running the code it still fails the input of [4,5,5,4] and returns [4,5,4].

New Code:

def purify(numbers):

    pure = numbers

    for num in pure:
        if num % 2 != 0:
            pure.remove(num)

    return pure
Jason T. Eyerly
  • 183
  • 1
  • 6
  • 18
  • 2
    Your code makes exactly the same mistake [the same classmate I linked to earlier did](http://stackoverflow.com/q/25326339/3001761) (maybe you should have read it?); **don't modify lists while iterating over them**. Also, `list.remove` is in-place, so will `return None`. Also, `pure = numbers` **does not** create a copy, so you aren't meeting the specification. – jonrsharpe Aug 15 '14 at 21:24
  • Make sure to post *valid* code that *accurately* reproduces the problem. Trivially, the indenting is incorrect and the presented code will not parse. – user2864740 Aug 15 '14 at 21:24
  • list.remove return None. It modifies the list instead. Also, do not modify the list while you are iterating over it – spicavigo Aug 15 '14 at 21:24
  • So if pure = numbers does not make a copy, and I can't modify a list while iterating over it, what are my options to not edit the original list and return a clean list? – Jason T. Eyerly Aug 15 '14 at 21:38
  • 1
    For the love of everything, *read the question* I keep providing a link to! There are several answers there that aren't unique to strings and vowels, and will work happily with lists of even and odd numbers. To copy a list, do e.g. `pure = numbers[:]`. Consider adding to an empty list instead of removing from a full one. Use `filter` or a list comprehension. – jonrsharpe Aug 15 '14 at 21:45
  • Removing the "pure =" solved the None issue but still did not return properly. Using the filter/list comprehension as explained in the marked answer was what I was looking for. Thanks again. – Jason T. Eyerly Aug 15 '14 at 21:59
  • Possible duplicate of [TypeError: 'NoneType' object is not iterable in Python](http://stackoverflow.com/questions/3887381/typeerror-nonetype-object-is-not-iterable-in-python) – tripleee Jun 12 '16 at 11:04

4 Answers4

8

The first problem is that you change the list while iterating over it. DO NOT change lists while you are iterating over them. You will miss out on some of the values in the loop.

The second problem is that list.remove acts in-place and returns None. Thus, when you do pure = pure.remove(num), you set pure to None, which is no longer iterable, which in turn is what causes the error. Instead, you can filter the list as follows

def purify(numbers):
    return [num for num in numbers if num % 2 == 0]
hlt
  • 6,219
  • 3
  • 23
  • 43
  • 1
    This is great. This is what I was looking for. Is this the filter function or list comprehension? I still don't grasp list comprehension which is why I ask. Thanks again! – Jason T. Eyerly Aug 15 '14 at 21:59
  • This is a list comprehension. It adds each item `num` from `numbers` to the new list (`num for num in numbers`), but only iff ([that's a word](https://en.wikipedia.org/wiki/Iff)) `num` is even (`if num % 2 == 0`). – hlt Aug 15 '14 at 22:01
2

Well that error means that python is trying to iterate over a None object.

The only iteration in your code is your for loop, so pure must be the None.

Also, don't forget to indent properly:

def purify(numbers):

    pure = numbers

    for num in pure:
        if num % 2 != 0:
            pure = pure.remove(num)

    return pure

So pure is either being defined as None when you set it equal to numbers or it's being defined when you assign it from pure.remove(num). When you look up the documentation for remove(num), you see that it operates in-place, so it doesn't return anything useful (None usually). What you want to do it remove the pure = part in the for loop. You might notice that you end up skipping elements in your loop, but that would a separate problem with this piece of code (modifying something you're iterating over).

0

I have no idea what pure's type is but it seems that it's a mutable object. pure.remove updates pure in place, and therefore returns None. You shouldn't set it into pure. Moreover, you shouldn't modify objects you iterate over during the iteration itself.

EDIT: The previous code snippet I put here was incorrect, as mentioned in the comments. I suggest you pick one of the methods mentioned in the other answers. If you're really interested in maintaining the general structure of your current solution, here is a workaround that uses the copy module:

import copy

def purify(numbers):
    pure = copy.copy(numbers) # a shallow copy
    for num in numbers:
        if num % 2 != 0:
            pure.remove(num)

    return pure

Note that numbers's class should implement the __copy__ method for this to work. You've mentioned in your comments above that numbers is a list, so the built-in implementation works for you. However, for a list you can simply do the following:

pure = numbers[:]
Yoel
  • 9,144
  • 7
  • 42
  • 57
-2
def purify(numbers):
    sequence = []     //starting with a an empty 
    for f in sequence:
        if f%2 -1:    //filtering out odd numbers,alternative to f%2 ==0
            sequence.append(f) //append each even number to the empty list
    return sequence

N/B:starting with an empty list ensures list is not modified

schroeder
  • 533
  • 1
  • 7
  • 25
wizards069
  • 1
  • 1
  • 1