мелкая копия в питоне - PullRequest
       45

мелкая копия в питоне

0 голосов
/ 13 октября 2018

Я немного запутался в том, как работает мелкая копия, я понимаю, что когда мы делаем new_obj = copy.copy(mutable_obj), создается новый объект, элементы которого все еще указывают на старый объект.

Пример того, где я нахожусьпутать -

## assignment
i = [1, 2, 3]
j = i
id(i[0]) == id (j[0])  # True
i[0] = 10
i  # [10, 2, 3]
j  # [10, 2, 3]

## shallow copy
k = copy.copy(i)
k   # [10, 2, 3]
id(i) == id(k)  # False (as these are two separate objects)
id(i[0]) == id (k[0])  # True (as the reference the same location, right?)
i[0] = 100
id(i[0]) == id (k[0])  # False (why did that value in that loc change?)
id(i[:]) == id (k[:])  # True  (why is this still true if an element just changed?)
i   # [100, 2, 3]
k   # [10, 2, 3]

В мелкой копии, k[0] не просто указывает на i[0] похоже на назначение?Не должно ли k[0] меняться при изменении i[0]?

Почему я ожидаю, что они будут такими же, потому что -

i = [1, 2, [3]]
k = copy(i)
i  # [1, 2, [3]]
k  # [1, 2, [3]]
i[2].append(4)
i  # [1, 2, [3, 4]]
k  # [1, 2, [3, 4]]
id(i[0]) == id (k[0])  # True
id(i[2]) == id (k[2])  # True
id(i[:]) == id (k[:])  # True

Ответы [ 3 ]

0 голосов
/ 13 октября 2018
  • В первом случае j = i - это присвоение, и j, и i указывают на один и тот же объект списка.Когда вы изменяете элемент объекта списка и печатаете i и j, так как и i, и j указывают на один и тот же объект списка, и это элемент, а не объект списка, который изменился, поэтому оба будут печатать один и тот же вывод.
  • Во втором случае k = copy.copy(i) - это поверхностная копия, в которой делается копия объекта списка и копия вложенных ссылок, но внутренние неизменяемые объекты не копируются.Мелкая копия не создает копию вложенных объектов, она просто копирует ссылку на вложенные объекты.Пожалуйста, укажите это https://www.programiz.com/python-programming/shallow-deep-copy
  • Таким образом, i и k имеют разный набор ссылок, указывающих на одни и те же неизменяемые объекты.Когда вы делаете i[0] = 100, ссылка в списке i указывает на новый объект int со значением 100, но ссылка в k по-прежнему ссылается на старый объект int со значением 10.
0 голосов
/ 13 октября 2018

id(i) == id(k) # False (as these are two separate objects)

Правильно.

id(i[0]) == id (k[0]) # True (as the reference the same location, right?)

Правильно.

i[0] = 100

id(i[0]) == id (k[0]) # False (why did that value in that loc change?)

Он изменился, потому что вы изменили его в предыдущей строке .i[0] был , указывая 10, но вы изменили его, указав на 100.Следовательно, i[0] и k[0] теперь больше не указывают на одно и то же место.

Указатели (ссылки) в одну сторону .10 не знает, что на это указывает.Также не 100.Это просто места в памяти.Поэтому, если вы измените , где первый элемент i указывает на, k не волнует (поскольку k и i не та же ссылка),Первый элемент k все еще указывает на то, на что он всегда указывал.

id(i[:]) == id (k[:]) # True (why is this still true if an element just changed?)

Этот элемент немного более тонкий, но учтите, что:

>>> id([1,2,3,4,5]) == id([1,2,3])
True

, тогда как

>>> x = [1,2,3,4,5]
>>> y = [1,2,3]
>>> id(x) == id(y)
False

Это связано с некоторыми тонкостями сборки мусора и идентификатора, и здесь подробно рассматривается: Безымянный объект Python имеет одинаковый идентификатор .

Короче говоря, когда вы говорите id([1,2,3,4,5]) == id([1,2,3]), первое, что происходит, мы создаем [1,2,3,4,5].Затем мы берем, где он находится в памяти с вызовом id.Однако [1,2,3,4,5] является анонимным, и сборщик мусора немедленно возвращает его.Затем мы создаем еще один анонимный объект, [1,2,3], и CPython решает, что он должен идти в месте, которое он только что очистил.[1,2,3] также немедленно удаляется и очищается.Если вы сохраните ссылки, однако, GC не сможет помешать, и тогда ссылки будут другими.

Пример изменяемых файлов

То же самое происходит с изменяемыми объектами, если вы переназначаете их.Вот пример:

>>> import copy
>>> a = [ [1,2,3], [4,5,6], [7,8,9] ]
>>> b = copy.copy(a)
>>> a[0].append(123)
>>> b[0]
[1, 2, 3, 123]
>>> a
[[1, 2, 3, 123], [4, 5, 6], [7, 8, 9]]
>>> b
[[1, 2, 3, 123], [4, 5, 6], [7, 8, 9]]
>>> a[0] = [123]
>>> b[0]
[1, 2, 3, 123]
>>> a
[[123], [4, 5, 6], [7, 8, 9]]
>>> b
[[1, 2, 3, 123], [4, 5, 6], [7, 8, 9]]

Разница в том, что когда вы говорите a[0].append(123), мы модифицируем все, на что указывает a[0].Случается, что b[0] указывает на один и тот же объект (a[0] и b[0] являются ссылками на тот же объект).

Но если вы указываете a[0] для нового объекта (через присваивание, как в a[0] = [123]), тогда b[0] и a[0] больше не указывают на то же место.

0 голосов
/ 13 октября 2018

В Python все вещи являются объектами.Это включает в себя целые числа.Все списки содержат только ссылки на объекты.Замена элемента списка не означает, что сам элемент изменяется.

Рассмотрим другой пример:

class MyInt:
    def __init__(self, v):
        self.v = v
    def __repr__(self):
        return str(self.v)

>>> i = [MyInt(1), MyInt(2), MyInt(3)]
[1, 2, 3]
>>> j = i[:] # This achieves the same as copy.copy(i)

[1, 2, 3]
>>> j[0].v = 7
>>> j
[7, 2, 3]
>>> i
[7, 2, 3]

>>> i[0] = MyInt(1)
>>> i
[1, 2, 3]
>>> j
[7, 2, 3]

Я создаю класс MyInt, который просто содержит int.Изменяя экземпляр класса, оба списка «меняются».Однако, когда я заменяю запись в списке, списки теперь другие.

То же самое происходит с целыми числами.Вы просто не можете их изменить.

...