Question

Je lis Comment penser comme un informaticien qui est un texte d'introduction pour « Python Programmation » .

Je veux clarifier le comportement de l'opérateur de multiplication (de *) lorsqu'il est appliqué à des listes.

Considérons la fonction 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

La sortie réelle est

[[0, 7], [0, 7], [0, 7], [0, 7]]

La version correcte de make_matrix est:

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

La raison pour laquelle la première version de make_matrix échoue (comme expliqué dans le livre à 9.8) est que

... chaque rangée est un alias des autres rangées ...

Je me demande pourquoi

[[0] * columns] * rows

cause ... chaque rangée est un alias des autres rangées ...

mais pas

[[0] * columns]

i.e.. pourquoi chaque [0] dans une rangée ne sont pas un alias de l'autre élément de ligne.

Était-ce utile?

La solution

TOUT en python sont des objets, et python ne fait jamais des copies à moins explicity demandé de le faire.

Quand vous faites

innerList = [0] * 10

vous créez une liste de 10 éléments, tous au même référant objet int 0 .

Comme des objets entiers sont immuable , quand vous faites

innerList[1] = 15

Vous changez le deuxième élément de la liste afin qu'il se réfère à un autre entier 15 . Cela fonctionne toujours à cause de int objets immuabilité.

Voilà pourquoi

outerList = innerList * 5

va créer un objet list avec 5 éléments, chacun d'eux est une référence à le même innerList comme ci-dessus. Mais puisque les objets sont list mutable :

outerList[2].append('something')

est le même que:

innerList.append('something')

Parce qu'ils sont deux références à même objet list . Ainsi, l'élément se retrouve dans ce seul list. Il semble dupliquer, mais le fait est qu'il n'y a qu'un seul objet list, et de nombreuses références à elle.

Par contre si vous faites

outerList[1] = outerList[1] + ['something']

Ici vous créer une autre objet list (en utilisant + des listes est une copie explicite), et l'attribution d'une référence à elle dans la deuxième position de outerList. Si vous « append » l'élément de cette façon (pas vraiment annexant, mais créer une autre liste), innerList ne sera pas affecté.

Autres conseils

listes ne sont pas primitives, ils sont passés par référence. Une copie de la liste est un pointeur vers une liste (en C jargon). Tout ce que vous faites à la liste arrive à toutes les copies de la liste et les copies de son contenu, sauf si vous faites une copie superficielle.

[[0] * columns] * rows

Oops, nous avons juste fait une grande liste de pointeurs sur [0]. Changer un et vous les modifiez tous.

Entiers ne sont pas passés par référence, ils sont vraiment copiés, donc [0] * contenu fait vraiment beaucoup de nouveaux 0 et les annexant à la liste.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top