-1

I have a list like this:

a_list = [['a', 1], ['b', 4], ['c', None]]

I need to replace all None values with 0 and replace any value that is not None to None. So above list will become:

modified_a_list = [['a', None], ['b', None], ['c', 0]]

I have code like this:

a_list = [['a', 1], ['b', 4], ['c', None]]
b_list = a_list
modified_a_list = []
for item in b_list:
    if item[1]==None:
        item[1]=0
        modified_a_list.append(item)
    else:
        item[1] = 0
        modified_a_list.append(item)
print a_list, modified_a_list

My output becomes:

a_list = [['a', None], ['b', None], ['c', 0]]
modified_a_list = [['a', None], ['b', None], ['c', 0]]

modified_a_list looks OK. But why a_list is changed as well?

vishes_shell
  • 22,409
  • 6
  • 71
  • 81
coding
  • 181
  • 6
  • 18

4 Answers4

0

This is a problem with list mutability. When you do b_list = a_list you're just copying a reference to a_list, so changes to b_list will happen in a_list too. Then the loop modifies the lists inside b_list.

Because there are nested lists, you can't just copy a_list with the slicing trick or anything like that. Easiest is just to use deepcopy:

from copy import deepcopy
a_list = [['a', 1], ['b', 4], ['c', None]]
b_list = deepcopy(a_list)
modified_a_list = []
for item in b_list:
    if item[1]==None:
        item[1]=0
        modified_a_list.append(item)
    else:
        item[1] = None
        modified_a_list.append(item)
print(a_list, modified_a_list)

Alternatively, you can just cut out the middleman and do a shallow copy of the inner lists inside the loop, i.e.:

a_list = [['a', 1], ['b', 4], ['c', None]]
modified_a_list = []
for item in a_list:
    temp=item[:]
    if item[1]==None:
        temp[1]=0
        modified_a_list.append(temp)
    else:
        temp[1] = None
        modified_a_list.append(temp)
print(a_list, modified_a_list)
Paul Gowder
  • 2,409
  • 1
  • 21
  • 36
0

Like others have said, when creating b_list it is basically a pointer to a_list. So when you work on b_list you are also working on a_list. If you want to leave a_list alone, you have to create a whole new list.

>>> a_list = [['a', 1], ['b', 4], ['c', None]]
>>> 
>>> modified_a_list = [[a[0], 0 if a[1] is None else None] for a in a_list]
>>> 
>>> a_list
[['a', 1], ['b', 4], ['c', None]]
>>> 
>>> modified_a_list
[['a', None], ['b', None], ['c', 0]]
Brobin
  • 3,241
  • 2
  • 19
  • 35
0

In the loop, item is a reference to an element of b_list, which refers to the a_list as well. So when you do item[1] = 0 in b_list, it sets the second element of item to 0, which is reflected in both references of the list.


    a_list \
            \
             ----> [ element1, element2, element3 ]
            /           |          |         |
    b_list /            |          |         |
                    ['a', 0]    ['b', 0]  ['c', None]

Sufian Latif
  • 13,086
  • 3
  • 33
  • 70
0

You can try this:

a_list = [['a', 1], ['b', 4], ['c', None]]

new_list = [[None if isinstance(b, int) else 0 if b == None else b for b in i] for i in a_list]

Output:

[['a', None], ['b', None], ['c', 0]]
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • @vishes_shell - actually it isn't, it's just inefficient as you don't need to check for instance, nor you need to loop through the whole second list. – zwer Jun 09 '17 at 15:50
  • @zwer are you kiddin me? Nested list comprehension is alright, but 2 if-else clauses in single list comprehension? Whot? – vishes_shell Jun 09 '17 at 15:55
  • @vishes_shell seems pretty straight-forward to read to me... it's needlessly loopy and inefficient, but it's not something that is unreadable. I've seen far worse offenders. – zwer Jun 09 '17 at 15:56
  • This is at least one solution, as it outputs the proper results. – Ajax1234 Jun 09 '17 at 16:07