-1

I made this function which removes all the odd numbers from a list. (I can't modify the original list)

Code:-

def purify(sequence):
   purified = sequence
   for i in purified:
       if i % 2 != 0:
          purified.remove(i)
   return purified

The function works on most of the list but if a list have same values like [4, 5, 5, 4] it returns [4, 5, 4].
I have changed the code to achieve what I required but I am just curious why this happens?

luciferchase
  • 559
  • 1
  • 10
  • 24
  • Possible duplicate of [Removing Item From List - during iteration - what's wrong with this idiom?](https://stackoverflow.com/questions/2896752/removing-item-from-list-during-iteration-whats-wrong-with-this-idiom) – Nick is tired May 11 '19 at 03:46

5 Answers5

2

You shouldn't try to remove elements while iterating. Also, purified = sequence doesn't actually create a copy of sequence. The best way to do this is to use a list comprehension like so:

def purify(sequence):
    purified = [i for i in sequence if i % 2 == 0]
    return purified
iz_
  • 15,923
  • 3
  • 25
  • 40
2

purified = sequence doesn't create a copy of sequence and assign to purified, rather both variables point to the same object, which you are trying to do. In addition, you are also modifying the same list you are iterating on, hence the weird behavior.

You have a few choices, and all of them work on Python 2.7

  • Make a copy of sequence via sequence[:] called list splicing, and iterate on it
def purify(sequence):

    #Iterate on copy of sequence
    for i in sequence[:]:
        if i % 2 != 0:
            sequence.remove(i)
    return sequence

print purify([4, 5, 5, 4])
#[4, 4]
  • Use list-comprehension to only pick even values from the list
def purify(sequence):

    #Pick only even values
    return [item for item in sequence if item%2 == 0]

print purify([4, 5, 5, 4])
#[4, 4]
  • Use filter to filter out odd values
def purify(sequence):
    # Iterate on copy of sequence
    return list(filter(lambda x:x%2==0, sequence))

print(purify([4, 5, 5, 4]))
# [4, 4]
Devesh Kumar Singh
  • 20,259
  • 5
  • 21
  • 40
1

(I could have sworn I answered a dupe of this, but I can't find it...)

Anyway, the reason is that you are modifying a sequence while iterating over it. You should do something like this instead:

# Python 3
def purify(sequence):
    purified = sequence.copy()
    for i in sequence:
        if i % 2 != 0:
            purified.remove(i)

    return purified

# Python 2
from copy import copy

def purify(sequence):
    purified = copy(sequence)
    for i in sequence:
        if i % 2 != 0:
            purified.remove(i)

    return purified

Alternatively, you could start with an empty list and add elements that satisfy your criteria:

def purify(sequence):
    purified = []
    for i in sequence:
        if i % 2 == 0:
            purified.append(i)

    return purified

Or, with a list comprehension, this:

purified = [i for i in sequence if i % 2 == 0]
gmds
  • 19,325
  • 4
  • 32
  • 58
  • I tried first method it threw the following error ```AttributeError: 'list' object has no attribute 'copy'``` And sorry but why ```for``` is indented? – luciferchase May 11 '19 at 03:22
  • @Lucifer That's not right. Try restarting your shell. The indentation is a mistake, fixed. – gmds May 11 '19 at 03:24
  • I tried this ```print purify([4, 5, 5, 4]```, it gave that error. Can you explain why? – luciferchase May 11 '19 at 03:28
  • @Lucifer Works fine for me. I believe you're using Python 2. I will edit my answer (the other two methods should work). – gmds May 11 '19 at 03:31
1

On python2.7 use the copy module.

Ex:

from copy import copy

def purify(sequence):
    purified = copy(sequence)
    for i in sequence:
        if i % 2 != 0:
            purified.remove(i)

    return purified
print purify([4, 5, 5, 4])
Rakesh
  • 81,458
  • 17
  • 76
  • 113
1

by using filter

# python 2.x
your_list= filter(lambda x: x % 2 == 0, your_list)

# python 3.x
your_list = list(filter(lambda x: x % 2 == 0, your_list))

print(your_list)

explanation:

  • lambda is a single line nameless function, returns if the number is even.
  • filter is test for truthfulness of that on your_list

cheers!

P.hunter
  • 1,345
  • 2
  • 21
  • 45