Let’s go in parts. First, let’s see what happens when you multiply a list by a number:
lista = [0, 0]
m = [lista] * 2
# m é uma lista contendo 2 listas
print(m) # [[0, 0], [0, 0]]
m[0][0] = 10 # mudar um elemento da primeira lista
print(m) # [[10, 0], [10, 0]] (mudei nas 2 listas)
print(id(m[0]), id(m[1])) # imprimem o mesmo número
The variable m
is a list of lists: it has 2 elements, and each of them is a list. Only when doing [lista] * 2
, both lists of m
point to the same list. Therefore a change in the first list also reflects in the second, since both point to the same list. So much so that the id
, the result will be the same number for both (according to documentation, in the specific case of Cpython, id
returns the address of the object in memory, so in the above code you can see that both point to the same list).
But and deepcopy
, shouldn’t I fix it? No:
lista = [0, 0]
m = [lista] * 2
print(m) # [[0, 0], [0, 0]]
print(id(m[0]), id(m[1])) # imprimem o mesmo número
from copy import deepcopy
m2 = deepcopy(m)
print(m2) # [[0, 0], [0, 0]]
print(id(m2[0]), id(m2[1])) # imprimem o mesmo número (não é o mesmo de m)
# mudar um elemento da primeira lista também afeta a segunda
m2[0][0] = 10
print(m2) # [[10, 0], [10, 0]]
# mas m permanece inalterado
print(m) # [[0, 0], [0, 0]]
deepcopy
created another list (so much so that the id
of those lists in m2
is different from m
), but m2
still has the same characteristic: the two lists he has are references to the same list.
This is because, according to the documentation, deepcopy
maintains an internal "memory" dictionary that stores objects that have already been copied. So if it finds the same object again, it uses this internal copy instead of copying again.
That is, first he copies the list that is in m[0]
and store in this internal "memory". When it will copy m[1]
, you will see that it is the same list (because we have seen that both point to the same place), so instead of copying again, it uses the copy that is in the internal "memory". The result is that m2
will have 2 references to the same list.
How to solve?
The simplest is to simply not create the list using *
. In fact, since lists in Python are dynamic and can change size as needed, you don’t even need to create them with a certain size. Why boot everything with zero if you will then read the values and overwrite everything? Simply add the elements as they are read:
n = int(input('Digite o tamanho da matriz: '))
m = []
for linha in range(n):
lista = []
for coluna in range(n):
numero = int(input(f'Digite um valor para a posição {linha},{coluna}: '))
lista.append(numero)
m.append(lista)
print(f'Matriz criada com sucesso: {m}')
And to create the transposed matrix, just iterate through all the lines simultaneously and create a list at each iteration:
mT = []
for elementos in zip(*m):
mT.append(list(elementos))
print(mT)
zip
iterates through multiple lists at once, and the syntax *m
serves to make the unpacking of the list m
. That is to say, zip(*m)
is the same as doing zip(m[0], m[1], etc..)
, but to do so I would have to know the size of m
. Using the unpacking, i don’t need to know the quantity, all are passed as arguments to zip
.
So in practice I’m passing all the lines of the matrix to zip
. Thus, in the first iteration, elementos
will be a tuple containing the first element of each line. In the second iteration, it will be a tuple with the second element of each, and so on.
With this, just create a list from this tuple and insert in mT
. At the end, we have the transposed matrix.
If you want, you can also use a comprehensilist on, much more succinct and pythonic:
mT = [ list(elementos) for elementos in zip(*m) ]
Anyway, you don’t need deepcopy
because it is not necessary to create m
with a predetermined size. But if you want much to do this, the solution is not to use *
to create it:
n = # ler o valor de n
# assim cada sublista é independente (não ocorre o problema de ambas apontarem para a mesma)
m = [ [0] * n for i in range(n) ]
for linha in range(n):
# Não precisa criar uma outra lista interna
for coluna in range(n):
m[linha][coluna] = int(input(f'Digite um valor para a posição {linha},{coluna}: '))
from copy import deepcopy
mT = deepcopy(m)
for i in range(n):
for j in range(n):
mT[j][i] = m[i][j]
print(mT)
Notice that I eat now m
has already been created with the right size, also do not need to create another list within the for
, because I can assign the value directly at the specific position.
But I still prefer to create the lists and add the "on-demand" elements, rather than create the matrix with zeros, fill it, create a copy, etc...
Perfectly explained!! Thank you very much for the lesson!!!
– Almir Lopes Martins
@Almirlopesmartins If the answer solved your problem, you can accept it, see here how and why to do it. It is not mandatory, but it is a good practice of the site, to indicate to future visitors that it solved the problem
– hkotsubo