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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top