Оператор умножения применяется к списку (структура данных) - PullRequest
11 голосов
/ 10 июня 2009

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

18 голосов
/ 10 июня 2009

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

Когда вы делаете

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 не будет затронут.

0 голосов
/ 10 июня 2009

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

[[0] * columns] * rows

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

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

...