Multiplicar operador aplicado a lista (estrutura de dados)
-
13-09-2019 - |
Pergunta
Estou lendo Como pensar como um cientista da computação que é um texto introdutório para "Python Programming" .
Quero esclarecer o comportamento do operador de multiplicação (*
) quando aplicado a listas.
Considere a função make_matrix
def make_matrix(rows, columns):
"""
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
>>> m = make_matrix(4, 2)
>>> m[1][1] = 7
>>> m
[[0, 0], [0, 7], [0, 0], [0, 0]]
"""
return [[0] * columns] * rows
A saída real é
[[0, 7], [0, 7], [0, 7], [0, 7]]
A versão correta do make_matrix é a seguinte:
def make_matrix(rows, columns):
"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
>>> m = make_matrix(4, 2)
>>> m[1][1] = 7
>>> m
[[0, 0], [0, 7], [0, 0], [0, 0]]
"""
matrix = []
for row in range(rows):
matrix += [[0] * columns]
return matrix
A razão pela qual primeira versão do make_matrix falhar (como explicado no livro em 9,8) é que
... cada linha é um apelido para as outras linhas ...
Eu quero saber porque
[[0] * columns] * rows
causas ... cada linha é um apelido para as outras linhas ...
mas não
[[0] * columns]
i. por que cada [0]
em uma linha não é um apelido para outro elemento de linha.
Solução
TUDO em python são objetos, e python nunca faz cópias a menos que explicitamente solicitado a fazê-lo.
Quando você faz
innerList = [0] * 10
criar uma lista com 10 elementos, todas elas se referindo ao mesmo objeto int
0
.
Uma vez que objetos inteiros são imutável , quando você faz
innerList[1] = 15
Você está alterando o segundo elemento da lista para que ele se refere a outro número inteiro 15
. Isso sempre funciona por causa de int
objetos imutabilidade.
É por isso
outerList = innerList * 5
criará um objeto list
com 5 elementos, cada um é uma referência a o mesmo innerList
apenas como acima. Mas desde que os objetos list
são mutável :
outerList[2].append('something')
é o mesmo que:
innerList.append('something')
Porque são duas referências ao mesmo objeto list
. Assim, o elemento acaba nessa única list
. Parece ser duplicada, mas o fato é que há apenas um list
objeto, e muitas referências a ele.
Por outro lado, se você fizer
outerList[1] = outerList[1] + ['something']
Aqui você está criar outro objeto list
(usando +
com listas é uma cópia explícito), e atribuir uma referência a ele para a segunda posição de outerList
. Se você "acrescentar" o elemento dessa maneira (não realmente acrescentar, mas a criação de uma outra lista), innerList
não será afetada.
Outras dicas
listas não são primitivos, eles são passados ??por referência. Uma cópia de uma lista é um ponteiro para uma lista (em gíria C). Qualquer coisa que você faz para a lista acontece com todas as cópias da lista e as cópias de seu conteúdo, a menos que você faça uma cópia superficial.
[[0] * columns] * rows
Opa, temos apenas fez uma grande lista de ponteiros para [0]. Alterar um e você mudá-los todos.
Os inteiros não são passados ??por referência, eles são realmente copiados, portanto, [0] * conteúdo está realmente fazendo um monte de novos 0 e anexando-os à lista.