Список питонов по значению, а не по ссылке
Давайте возьмем пример
a=['help', 'copyright', 'credits', 'license']
b=a
b.append('XYZ')
b
['help', 'copyright', 'credits', 'license', 'XYZ']
a
['help', 'copyright', 'credits', 'license', 'XYZ']
Я хотел добавить значение в список "b", но значение списка "a" также изменилось.
Я думаю, что понятия не имею, почему это так (Python передает списки по ссылке).
Мой вопрос: "Как я могу передать его по значению, чтобы добавление" b "не изменило значения в" a "?"
11 ответов
Для копирования списка вы можете использовать list(a)
или же a[:]
, В обоих случаях создается новый объект.
Эти два метода, однако, имеют ограничения для коллекций изменяемых объектов, поскольку внутренние объекты сохраняют свои ссылки без изменений:
>>> a = [[1,2],[3],[4]]
>>> b = a[:]
>>> c = list(a)
>>> c[0].append(9)
>>> a
[[1, 2, 9], [3], [4]]
>>> c
[[1, 2, 9], [3], [4]]
>>> b
[[1, 2, 9], [3], [4]]
>>>
Если вам нужна полная копия ваших объектов, вам нужно copy.deepcopy
>>> from copy import deepcopy
>>> a = [[1,2],[3],[4]]
>>> b = a[:]
>>> c = deepcopy(a)
>>> c[0].append(9)
>>> a
[[1, 2], [3], [4]]
>>> b
[[1, 2], [3], [4]]
>>> c
[[1, 2, 9], [3], [4]]
>>>
С точки зрения производительности мой любимый ответ будет:
b.extend(a)
Проверьте, как связанные альтернативы сравниваются друг с другом с точки зрения производительности:
In [1]: import timeit
In [2]: timeit.timeit('b.extend(a)', setup='b=[];a=range(0,10)', number=100000000)
Out[2]: 9.623248100280762
In [3]: timeit.timeit('b = a[:]', setup='b=[];a=range(0,10)', number=100000000)
Out[3]: 10.84756088256836
In [4]: timeit.timeit('b = list(a)', setup='b=[];a=range(0,10)', number=100000000)
Out[4]: 21.46313500404358
In [5]: timeit.timeit('b = [elem for elem in a]', setup='b=[];a=range(0,10)', number=100000000)
Out[5]: 66.99795293807983
In [6]: timeit.timeit('for elem in a: b.append(elem)', setup='b=[];a=range(0,10)', number=100000000)
Out[6]: 67.9775960445404
In [7]: timeit.timeit('b = deepcopy(a)', setup='from copy import deepcopy; b=[];a=range(0,10)', number=100000000)
Out[7]: 1216.1108016967773
Также вы можете сделать:
b = list(a)
Это будет работать для любой последовательности, даже для тех, которые не поддерживают индексаторы и фрагменты...
Если вы хотите скопировать одномерный список, используйте
b = a[:]
Однако если a
это двумерный список, это не будет работать для вас. То есть любые изменения в a
также будет отражено в b
, В этом случае используйте
b = [[a[x][y] for y in range(len(a[0]))] for x in range(len(a))]
Как упомянул Phihag в своем ответе,
b = a[:]
будет работать для вашего случая, так как нарезка списка создает новый идентификатор памяти списка (то есть вы больше не ссылаетесь на один и тот же объект в вашей памяти, и изменения, которые вы вносите в один, не будут отражены в другом).
Однако есть небольшая проблема. Если ваш список многомерен, как в списках внутри списков, простое нарезание не решит эту проблему. Изменения, сделанные в более высоких измерениях, то есть списки в исходном списке, будут разделены между ними.
Не волнуйтесь, есть решение. Копия модуля имеет отличную технику копирования, которая решает эту проблему.
from copy import deepcopy
b = deepcopy(a)
скопирует список с новым идентификатором памяти независимо от того, сколько уровней он содержит!
Когда вы делаете b = a
вы просто создаете другой указатель на ту же память a, поэтому, когда вы добавляете b, тоже меняется.
Вам нужно создать копию, и это делается так:
b = a[:]
Я обнаружил, что мы можем использовать extend() для реализации функции copy()
a=['help', 'copyright', 'credits', 'license']
b = []
b.extend(a)
b.append("XYZ")
Я бы порекомендовал следующее решение:
b = []
b[:] = a
Это скопирует все элементы из a в b. Копия будет копией значения, а не эталонной.
b = list(a)