Оператор Multiply, применяемый к списку (структура данных)

Я читаю как думать, как компьютерщик который является вводным текстом для "программирования Python".

Я хочу уточнить поведение оператора умножения (*) при применении в списки.

рассмотрим функцию 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

фактический выход составляет

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

правильная версия make_matrix - это :

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

Причина первая версия make_matrix терпит неудачу ( как объясняется в книге в 9.8 ) , это

...каждая строка является псевдонимом других строк...

интересно, почему

[[0] * columns] * rows

причины ...каждая строка является псевдонимом других строк...

а не

[[0] * columns]

то есть почему каждый [0] в строке не является псевдонимом другого элемента строки.

2 ответов


все в python являются объектами, и python никогда не делает копий, если explicity не попросит об этом.

когда вы

innerList = [0] * 10

создать список из 10 элементов, все они ссылаются на одно и то же int объект 0.

так как число объектов неизменяемые, когда вы делаете

innerList[1] = 15

вы меняете второй элемент списка, чтобы он ссылался на другой целое число 15. Это всегда работает из-за int иммутабельность объектов.

вот почему

outerList = innerList * 5

создать list объект с 5 элементами, каждый из которых является ссылкой на тот же innerList как и выше. Но с list объекты mutable:

outerList[2].append('something')

- это то же, что:

innerList.append('something')

потому что это две ссылки на то же самое list объект. Таким образом, элемент заканчивается в этом одном list. Похоже, что он дублируется, но факт в том, что есть только один list объект и множество ссылок на него.

по контрасту, если вы делаете

outerList[1] = outerList[1] + ['something']
вы создания другое list объект (через + со списками является явной копией), и присвоение ссылки на нее во вторую позицию outerList. Если вы" добавляете " элемент таким образом (не действительно добавляя, но создавая другой список),innerList будет в силе.

списки не являются примитивами, они передаются по ссылке. Копия списка-это указатель на список (на жаргоне C). Все, что вы делаете со списком, происходит со всеми копиями списка и копиями его содержимого, если вы не делаете мелкую копию.

[[0] * columns] * rows

Oops, мы только что сделали большой список указателей на [0]. Измените одного, и вы измените их всех.

целые числа не передаются по ссылке, они действительно копируются ,поэтому [0] * содержимое действительно делает много новых 0 и добавляю их в список.