Python - изменить или перезаписать ссылку на объект

72
12

Может ли кто-нибудь объяснить изменение или замену ссылки на объект легко понятным способом? Вот пример того, что я имею в виду:

Изменив ссылку на объект:

nested_list = [[]]*3
nested

result:
[[], [], []]
# now let me **modify** the object reference
nested[1].append('zzz')

result:
[['zzz'], ['zzz'], ['zzz']]

Переписывая ссылку на объект:

nested_list = [[]]*3
nested

result:
[[], [], []]
# now let me **modify** the object reference
nested[1] = ['zzz']

result:
[[], ['zzz'], []]

Означает ли это, что при использовании "append" мы только модифицируем ссылку на объект при использовании присвоения значений, т.е.

nested[1] = ['zzz']

мы перезаписываем значение и присваиваем вложенную [1] новой ссылке на объект? Это вызвано основной разницей между методом "добавить" и присвоением значений? Если да, то какая разница?

спросил(а) 2017-05-14T18:48:00+03:00 3 года, 4 месяца назад
1
Решение
69

Позвольте назначить имя x пустующему списку, чтобы было легче рассуждать о коде.

В первом примере

>>> x = []
>>> nested = [x]*3
>>> nested
[[], [], []]

вы создаете список, nested с тремя ссылками на x. Здесь доказательство:

>>> all(e is x for e in nested)
True

Мы создали только один пустой список x, поэтому

nested[0].append('zzz')
nested[1].append('zzz')
nested[2].append('zzz')

а также

x.append('zzz')

все эквивалентны и добавляются к одному списку в памяти:

>>> nested[0].append('zzz')
>>> nested
[['zzz'], ['zzz'], ['zzz']]
>>> nested[1].append('zzz')
>>> nested
[['zzz', 'zzz'], ['zzz', 'zzz'], ['zzz', 'zzz']]
>>> nested[2].append('zzz')
>>> nested
[['zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz']]
>>> x.append('zzz')
>>> nested
[['zzz', 'zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz', 'zzz']]

Второй пример прост. Вы создаете список, nested который изначально содержит три ссылки на один и тот же пустой список.

Затем вы перезаписываете то, на что ссылается второй элемент nested (т.е. nested[1]), вызывая

>>> x = []
>>> nested = [x]*3
>>> nested[1] = ['zzz']
>>> nested
[[], ['zzz'], []]

Второй элемент nested - это новый список, который не имеет никакого отношения к первому и третьему элементам nested.

>>> nested[0] is nested[1]
False
>>> nested[2] is nested[1]
False
>>> nested[0] is nested[2]
True

Поскольку вы не модифицировали nested[0] и nested[2] ссылки, они все еще держат один и тот же пустой список (который в нашем примере также называется именем x).

>>> x.append('x')
>>> nested
[['x'], ['zzz'], ['x']]

ответил(а) 2017-05-14T19:02:00+03:00 3 года, 4 месяца назад
59

Как я писал в своем комментарии, оператор * в list просто копирует ссылки внутри списка:

nested_list = [[]] * 3

Все три элемента внутри nested_list относятся к одному списку. Это имеет смысл, если вы думаете о том, что действительно говорит выражение выше. Оценка действительно происходит в следующем порядке:

nested_list = [[]]  # first create a list with an empty list.
nested_list = nested_list * 3 # duplicate the references to that empty list

Во вторую часть вашего вопроса. Если вы замените второй элемент на новый список:

nested_list[1] = ['zzz']

Первый и третий элементы относятся к одному и тому же пустым спискам, но только что назначенный (['zzz']) - это новый список (с одним элементом - 'zzz').

Например, если вы сделаете следующее, вы увидите, что первый и третий все еще относятся к одному и тому же списку:

nested_list[0].append('a')
print(nested_list)
# [['a'], ['zzz'], ['a']]
Решение

Чтобы создать три разных пустых списка, которые, вероятно, вы хотите, вы обычно делаете что-то вроде (следующие строки эквивалентны):

nested_lists = [[] for _ in range(3)]
nested_lists = [[], [], []]

ответил(а) 2017-05-14T19:03:00+03:00 3 года, 4 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема