12 Practice Makes Perfect


#1
def purify(list):
  if len(list) > 1:
    for i in range(len(list)-1):
      print list[i]
      print list[i] % 2
      if list[i] % 2 == 1:
        
        list.remove(list[i])
  else:
    list = []
  return list

Hello I keep getting this error with my above code:

Your function fails on purify([4, 5, 5, 4]). It returns [4, 5, 4] when it should return [4, 4].

I’m kind of confused why it’s able to remove other odd numbers from other lists but not this last one. I did the test print, and the 5 is indeed caught to meet the “if list[i] % 2 == 1” condition and yet is left in the list?? Can someone please tell me what I’m getting wrong here?


#2

Removing elements from the list you are iterating will give unexpected results.


#3

OK so I made it work with the following code:

def purify(list):
  newlist = []
  if len(list) > 1:
    for i in range(len(list)):
      if list[i] % 2 == 0:
        
        newlist.append(list[i])
  else:
    newlist = []
  return newlist

I feel like this is the easier way to do it though, and I was trying to make my initial way work because modifying the original list and returning that list seems harder/trickier to get


#4

Actually, that condition:

if len(list) > 1:

Doesn’t capture the possibility of a single item list with an even number since it will give a blank list, which is incorrect…it should give the single item list back. I’m going to to modify my code to account for this short coming (even though the CA solver accepts it)


#5

Repetitive and redundant. If the loop does not run, the list is just returned empty as initialized.


#6

Technically, the length of the list does not matter. In either form of for loop, the new list is unchanged. We do not need a conditional for this.

if ... === ...:
    ... .append(...)

It makes for short work when all we are doing is reading from a list. We don’t need to index, only the item at that index. in can iterate over both.

>>> numbers = range(10)
>>> new_list = []
>>> for number in numbers:
    if number % 2: continue
    new_list.append(number)

    
>>> new_list
[0, 2, 4, 6, 8]
>>> 

#7

You are absolutely right! The list gets appended if and only if the the condition inside the loop is met, and since a list with a single odd number doesn’t meet that if condition, the empty newlist gets returned. I missed this fact… so it just condenses down to:

def purify(list):
  newlist = []
  for i in range(len(list)):
    if list[i] % 2 == 0:
      newlist.append(list[i])
  return newlist

And it works


#8

Wow so if you just type “if number % 2” then that’s the same as if I wrote out the “== 0” portion. That’s good to know. Also, that “: continue” is a really nice trick. I didn’t know these syntax tricks. I’m liking python a lot :slight_smile:

Thanks again for your help @mtf!


#9

As expected. One thing to keep in mind from the standpoint of error prevention is the avoidance of reserved words when naming variables. list is not a good name to use since that is the name off a built in function (standard library function).

We already mentioned the item lookup approach for read-only loops and if you rewrite your code in that form you will see how brief it becomes and the less potential typos that result from less typing. Either form is valid and correct. But the read-only loop is much easier to type and to read. Just saying.

Yes, and no. More like the opposite.

x % 2  =>  non-zero if odd, so True on evaluation

x % 2 == 0  =>  zero if even, so True on evaluation.

Since parity is a binary construct, we can narrow this,

if x % 2 == 0:
    return "Even"
else:
    return "Odd"

down to this,

if x % 2:
    return "Odd"
else:
    return "Even"

Which thanks to the beauty of the Python Ternary we get,

return "Odd" if x % 2 else "Even"

Recall that if coerces any expression to a boolean.


#10
def purify(list):
  newlist = []
  for i in range(len(list)):
    if not list[i] % 2: continue
			newlist.append(list[i])
	return newlist

I modified my code to take your syntax into account but it’s stuck on that silly indent bug it seems, won’t execute. But is that my code looking correct now if I were to try to use the same syntax? I have to put the “not” in there since it’s the reverse right?


#11

That will skip even numbers.

As for indent, use four spaces for each block level, then your code will be consistent.


Edit

Consider the following with x being 3…

if not x % 2: continue

x % 2  =>  1
not 1  =>  0
if 0: continue  =>  outcome, no follow

y.append(x)  => odd number is appended

Now let x = 4

x % 2  => 0
not 0  =>  1
if 1: continue  =>  even number, followed

Nothing is appended to the new list when x is even.

The thing to note about the if statement in the earlier example is that it is a completed statement. There is no more to it, so the next line is at the same block level.

if condition: continue
y.append(x)

The return line is rarely seen inside a loop unless it is conditional. Your return line is inside the loop. It should have the same level of indent as the opening line of the for loop.

>>> def foo(bar):
    y = []
    for x in bar:
        if x % 2: continue
        y.append(x)
    return y

>>> foo(range(20))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> 

Taking it a wee bit further without getting into advanced concepts,

>>> def is_even(n):
    return n % 2 is 0

>>> is_even(42)
True
>>> def purify(array, slice=[]):
    for x in array:
        slice += [x] if is_even(x) else []
    return slice

>>> purify(range(20))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> 

We’ve learned up to now that we can extend a list using the list.append() method. Above we’ve seen that we can use the compound assignment of another list to augment the existing one. This is not something we can do in JavaScript but in Python it’s perfectly feasible.

>>> [1,2,3] + [4,5,6]
[1, 2, 3, 4, 5, 6]
>>> 

#12

Ahh… so this is the part that I wasn’t understanding fully. “x % 2” defaults to 0, i.e. even number, so all we need to do then is just append that number. See, I’m learning the little grammar and syntax used for python’s coding logic, this is great. Thanks for all your help again!


#13

There is no default in a modulo expression, only an absolute, the remainder. It’s 1 it’s 0.


#14

sorry I meant when you put in an if statement…so

if x % 2:

is the equivalent of

if x % 2 == 0:

is what I meant


#15

No they are not equivalent, but opposite

x % 2  

will be 1 when x is odd. 1 evaluates to True when cast as a boolean.

x % 2 == 0

will be True when x is even.


#16

Thanks @mtf! That direct jump is actually one of the things I’m loving about python. If it was VBA, for example, that would have to be explicitly spelled out for the computer to understand, whereas python just converts the 1 or 0 to boolean automatically.


#17

Proviso: when casting is inferred a boolean construct needs to be present.

x % 2

can be 0 or 1. At this point they could be “Jazz” or “Classical”. It has no boolean context.

if ...

changes the picture; a boolean context is asserted. Likewise,

return True and x % 2

#19

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.