Python: копирование списка в списке
Надеюсь, что кто-то может помочь мне здесь.
Я очень новичок в Python, и я пытаюсь понять, что я делаю неправильно.
Я уже искал и выяснил, что переменные Python могут быть связаны так, что изменение одного изменяет другое, и я провел многочисленные тесты с помощью функции id()
, чтобы справиться с этой концепцией. Но я, похоже, нашел исключение, я надеюсь, кто-то может объяснить...
во-первых, следующее работает, как ожидается, сделать независимую копию списка.
>>> a = [0,0]
>>> b = a[:]
>>> print a is b
False
>>> b[0]=1
>>> print a
[0,0]
>>> print b
[1,0]
Но если я немного изменил это так, чтобы a
был списком в списке, он меняет...
>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> print a is b
False
>>> b[0][0]=1
>>> print a
[[1, 0], [0, 0]]
>>> print b
[[1, 0], [0, 0]]
Теперь мы видим, что любое обновление b
также относится к a
, но результат print a is b
возвращает False
? Я также проверял это на id()
, все говорит, что они независимы друг от друга, но когда я обновляю одно, это относится и к другому.
Может ли кто-нибудь объяснить это?
Отмечая, что я запускаю их из http://labs.codecademy.com/#:workspace, поэтому моя первая мысль заключается в том, что это просто ошибка на их сайте, но я не знаю Не знаете?
EDIT:
СПАСИБО ВСЕМ за отличные ответы. Это было быстро! Я знаю, что это, вероятно, было задано раньше, но было трудно найти.
Поскольку все ответы верны, я буду ждать за день до маркировки. кто имеет наибольшее +1, получит отметку:)
Ответы
Ответ 1
b = a[:]
создает мелкую копию a
, поэтому изменение измененных списков в b
по-прежнему влияет на те же самые списки в a
.
Другими словами, a
и b
не указывают на один и тот же список (именно поэтому a is not b
), а скорее на два разных списка, которые содержат одинаковые два списка. Вы меняете один из этих списков с помощью b[0][0] = 1
, и это изменение отображается в a
.
Вы упомянули, что вы играли с id()
, поэтому взгляните на это:
>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> id(a)
2917280 # <----+
>>> id(b) # |----- different!
2771584 # <----+
>>> id(a[0]), id(a[1])
(2917320, 2917360) # <----+
>>> id(b[0]), id(b[1]) # |----- same!
(2917320, 2917360) # <----+
Ответ 2
Вам нужно сделать глубокую копию вашего списка. a[:]
делает только мелкую копию - см. документы
Вы можете использовать функцию copy.deepcopy
:
>>> import copy
>>> a = [[0,0],[0,0]]
>>> b = copy.deepcopy(a)
>>> b
[[0, 0], [0, 0]]
>>> b[0][0]=1
>>> a
[[0, 0], [0, 0]]
Ответ 3
Я считаю, что самый простой способ получить то, что происходит, это использовать визуальное представление (идея этого представления не моя, хотя мне она нравится).
Прежде всего, вам нужно понять, что в python есть только ссылки на объекты.
Сами объекты живут отдельно друг от друга. Например, список [0, 1]
- это список-объект, содержащий ссылки на объект 0
и объект 1
.
Ссылка - это своего рода ссылка. Это отличается от переменных на других языках, поскольку переменные обычно являются ячейками памяти, в которые вы помещаете вещи. В python "переменная", то есть идентификатор, является просто "именем" (= ссылкой) для объекта.
Чтобы понять это, давайте посмотрим на отношения между объектами с метафорой:
Скажем, объекты - это тяжелые скалы в море, которые связаны веревками и крючками (¿).
На поверхности моря живут идентификаторы, которые относятся к объектам. Идентификаторы - это буи, которые препятствуют погружениям объектов в глубины (где, скажем, морские монстры (они же Мусорщик) уничтожают их).
Например, мы можем представить эту ситуацию:
a = [0, 1]
Со следующей диаграммой:
___
( )
~~~~~~~~( a )~~~~~~~~
(___)
o ¿ o
| O
| o
|
|
+------+-------+
| [ ¿ , ¿ ] |
+----|-----|---+
| |
| |
o | |
O | |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
o O o
)
( ) o
) )( ) ( (
( ( )( ( ( ) )
Как вы можете видеть, идентификатор a
ссылается, т.е. связан с канатом, на объект списка.
Объект list имеет два слота, каждый из которых содержит ссылку, связанную с объектами 0
и 1
.
Теперь, если бы мы сделали:
b = a
Идентификатор b
будет ссылаться на тот же объект a
:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
¿ ¿
\ /
o \ / o
o \ / o
-------+-------
O | [ ¿ , ¿ ] | O
----|-----|----
| |
+-+-+ +-+-+
o | 0 | | 1 |
+---+ +---+ o
O
o O
o
)
) ( ) (
( ( )( ( ( )
( ) ) ( ) ( ( ) ) ( )
Если вместо этого сделайте небольшую копию a
, используя:
b = a[:]
Создается новый список, а его элементы - копии ссылок на объекты, на которые ссылается a
, т.е. вы сделали копии веревок, но они указывают на те же элементы:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| | | |
\ \ / /
\ \ / /
\ \ / / o
o \ \ / / o
\ \ / / o
o \ \ / /
\ \ / / o
O \ X /
\ / \ /
\/ \/
| |
| |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
Поскольку целые числа неизменны, нет никакой разницы между копиями или одинаковыми идентичными объектами, но при замене целых чисел на list
s, которые изменяются, вы в конечном итоге изменяете ссылки на один и тот же объект, следовательно, поведение, которое вы видите.
Визуально, код:
a = [[0, 1], [0, 1]]
b = a[:]
Результаты в:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| X |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| | \ |
| | | |
+----+-----+----+ +-----+----+----+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ | / /
| |/ /
| X /
| / | /
| / | /
\ / \ /
Y Y
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
Обратите внимание, что список b
относится к тем же подспискам a
.
(подробности реализации: компилятор CPython bytecode будет оптимизировать литералы, так что те же объекты 0
и 1
используются в обоих подсписок. Также существует некоторое кэширование для небольших целых чисел, но это не важно. подписи не имеют всех общих элементов).
Глубокая копия - это копия, которая позволяет избежать совместного использования одинаковых объектов.
Например, после выполнения:
import copy
a = [[0, 1], [0, 1]]
b = copy.deepcopy(a)
Ситуация такова:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ -------+------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
+----+----------+ +--+------------+ +----+----------+ +--+------------+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] | | [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+ +----|-----|----+ +----|-----|----+
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ | / / \ | / /
| |/ / | |/ /
| X / | X /
| / | / | / | /
| / | / | / | /
\ / \ / \ / \ /
Y Y Y Y
| | | |
+-+-+ +-+-+ +-+-+ +-+-+
| 0 | | 1 | | 0 | | 1 |
+---+ +---+ +---+ +---+
) )
( ( ) ( ( ( ) (
)( ) ) ) ( ( ) ) ) )( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( ( ( ) ( ) ( ( ( ( ) ) ( ) ( ( (
(На самом деле кажется, что copy.deepcopy
достаточно умен, чтобы избежать копирования неизменяемых встроенных объектов, таких как int
, long
, tuple
неизменяемых объектов и т.д.
поэтому все подсписцы имеют одни и те же объекты 0
и 1
)
Обратите внимание, что эти диаграммы также могут помочь вам понять, как работает подсчет ссылок.
Каждая веревка является ссылкой, и пока объект не имеет цепочку ссылок, которая поднимается до буя (то есть идентификатор), он остается живым. Когда больше нет веревок, чтобы связать объект с поверхностными буями, тогда объекты погружаются и уничтожаются сборщиком мусора.
Ответ 4
a
- список списков. Когда вы делаете b=a[:]
, вы создаете новый список, но копируете элементы. Таким образом, b
- это другой список, но элементы (подписи) одинаковы.
Ответ 5
В обоих случаях создается независимый список. Таким образом, a is b
всегда false.
В первом случае вы помещаете некоторые другие значения в один из списков.
Во втором случае ваши списки содержат одинаковые значения.
Как будто вы пишете
l = []
a = [l, l]
b = [l, l]
a is not b
, и тем не менее они содержат одни и те же данные.
Если вы измените l
сейчас, это изменение будет видно через все a[0]
, a[1]
, b[0]
и b[1]
.
Ответ 6
Хотя a is b
возвращает False
, a[0] is b[0]
возвращает True
. Поэтому, когда вы меняете b[0]
, вы обязательно меняете a[0]
>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> # a[0] is b[0]
>>> print a[0] is b[0]
True
>>> a.append('more stuff')
>>> print a
[[0, 0], [0, 0], 'more stuff']
>>> print b
[[0, 0], [0, 0]]
Ответ 7
Есть альтернатива дорогостоящей дорогостоящей глубине, когда вы имеете дело со списками внутри списков
origvector=[]
for ind in range(0, len(testvector)):
origvector.append(testvector[ind][:])
В этом примере "testvector" представляет собой матрицу из n векторов, каждый элемент содержит список из трех элементов. Вот так:
{0,1,2}{10,20,30}
{3,4,5}{40,50,60}
{6,7,8}{70,80,90}