Error modifying elements in sublists, all end with the same content

1

Someone could tell me why I have an error in the following code:

tabla=[]
linea=['0']
for k in range (3):
    linea.append('')
for k in range(12):
    tabla.append(linea)
i=0
for k in ['1','2','3','4','5','6','E','F','P','G','2G','T']:
    tabla[i][0] = k
    i+=1
print(tabla)

The result I get is:

[['T', '', '', ''], ['T', '', '', ''], ['T', '', '', ''],
 ['T', '', '', ''], ['T', '', '', ''], ['T', '', '', ''],
 ['T', '', '', ''], ['T', '', '', ''], ['T', '', '', ''],
 ['T', '', '', ''], ['T', '', '', ''], ['T', '', '', '']

instead of the one I'm looking for:

[['1', '', '', ''], ['2', '', '', ''], ['3', '', '', ''],
 ['4', '', '', ''], ['5', '', '', ''], ['6', '', '', ''],
 ['E', '', '', ''], ['F', '', '', ''], ['P', '', '', ''],
 ['G', '', '', ''], ['2G', '', '', ''], ['T', '', '', '']

As you can see, all the sublists contain "T" as the first element, instead of containing the corresponding value assigned in for with tabla[i][0] = k . What am I doing wrong?

    
asked by LuisAlberto 27.09.2017 в 17:26
source

1 answer

2

The problem is that all the sublists that your list contains tabla are actually the same list, they have the same reference. When you modify a value in one the rest are also modified since they are actually the same object. When your cycle finishes you assign the value "T" to the first element of the last "line", as in fact all the lines are the same list is reflected in all.

Simply add a copy of your línea list each time you add one to the matrix:

tabla.append(linea[:])

Slicing or slicing is used to return a shallow copy so that now each sublist of tabla is a different object, a different list. It is equivalent to:

import copy

tabla.append(copy.copy(linea))

One thing must be taken into account. The fila list is copied but its content is not copied, the same references are used. In your case they only seem to get immutable objects (strings) so if you modify a string it will not be reflected in the other lists. This is because "modifying" an immutable object involves creating a new object.

This does not happen if they were mutable objects, in this case you should use a deep copy , so that both the list and all the objects it contains are copied recursively:

import copy

tabla.append(copy.deepcopy(linea))

An alternative to doing what you do using compression lists would be:

tabla = [[elem] + ["" for _ in range(3)]
            for elem in ['1', '2', '3', '4', '5', '6', 'E', 'F', 'P', 'G', '2G', 'T']]

Edit:

In case you are interested you can see the behavior described above in detail with an example:

>>> lista1 = [1,  "a",  (1,  2),  ["c"]]
>>> lista2 = lista1

This does not copy lista1 , it simply assigns the same reference to variable lista2 . Both identifiers point to the same list, to the same object in memory. We can see it using id :

>>> id(lista1)
140001243246088
>>> id(lista2)
140001243246088

Modifying one implies modifying the other (both are the same in reality):

>>> lista1[0] = 147
>>> lista1
[147, 'a', (1, 2), ['c']]
>>> lista2
[147, 'a', (1, 2), ['c']]

Now let's see what a shallow copy is:

>>> lista2 = lista1[:] # equivalente a lista2 = copy.copy(lista1)

In this case lista1 and lista2 are two different objects:

>>> id(lista1)
140001243246088
>>> id(lista2)
 140001243245192

But not the elements they contain:

>>> id(lista1[0])
140001273510848
>>> id(lista2[0])
140001273510848

If we modify an immutable element, the change is only reflected in the list that is applied, since it implies the creation of a new object. If we modify a mutable object as a list, the change is reflected in both lists:

>>> lista1[0] += 777
>>> lista1[3].append("Hola") 
>>> lista1
[924, 'a', (1, 2), ['c', 'Hola']]
>>> lista2
[147, 'a', (1, 2), ['c', 'Hola']]

For the mutable objects to be different objects we need a deep copy:

>>> import copy
>>> lista2 = copy.deepcopy(lista1)
>>> lista1
[777, 'a', (1, 2), ['c', 'Hola']]
>>> lista2
[777, 'a', (1, 2), ['c', 'Hola']]
>>> lista1[3].append("Mundo")
>>> lista1
[777, 'a', (1, 2), ['c', 'Hola', 'Mundo']]
>>> lista2
[777, 'a', (1, 2), ['c', 'Hola']]
    
answered by 27.09.2017 / 17:36
source