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

StackOverflow https://stackoverflow.com/questions/974931

  •  13-09-2019
  •  | 
  •  

Вопрос

я читаю Как думать как ученый-компьютерщик это вводный текст по «Программированию на 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] в строке не является псевдонимом другого элемента строки.

Это было полезно?

Решение

ВСЕ в Python являются объектами, и Python никогда не создает копии, если об этом явно не попросят.

Когда ты это делаешь

innerList = [0] * 10

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

Поскольку целочисленные объекты неизменный, когда ты это делаешь

innerList[1] = 15

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

Вот почему

outerList = innerList * 5

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

outerList[2].append('something')

Такой же как:

innerList.append('something')

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

Напротив, если вы это сделаете

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

Вот, пожалуйста создание другой list объект (с использованием + со списками является явной копией), и присвоение ссылки на него во вторую позицию outerList.Если вы «добавите» элемент таким образом (на самом деле не добавляя, а создавая другой список), innerList не будет затронуто.

Другие советы

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

[[0] * columns] * rows

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

Целые числа не передаются по ссылке, они на самом деле копируются, поэтому содержимое [0] * на самом деле создает множество НОВЫХ 0 и добавляет их в список.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top