Каковы варианты клонирования или копирования списка в Python?
Использование new_list = my_list
затем изменяет new_list
каждый раз, когда изменяется my_list
.
Почему это?
С new_list = my_list
вас фактически нет двух списков. Назначение просто копирует ссылку на список, а не фактический список, поэтому как new_list
, так и my_list
относятся к тому же списку после назначения.
Чтобы на самом деле скопировать список, у вас есть различные возможности:
Вы можете использовать встроенный list.copy()
(доступный с python 3.3):
new_list = old_list.copy()
Вы можете отрезать его:
new_list = old_list[:]
По мнению Алекса Мартелли (по крайней мере, в 2007 году), это странный синтаксис, и использовать его никогда не имеет смысла. ;) (По его мнению, следующий - более читаемый).
Вы можете использовать встроенную функцию list()
:
new_list = list(old_list)
Вы можете использовать generic copy.copy()
:
import copy
new_list = copy.copy(old_list)
Это немного медленнее, чем list()
потому что сначала нужно выяснить тип данных old_list
.
Если список содержит объекты и вы хотите их скопировать, используйте общий copy.deepcopy()
:
import copy
new_list = copy.deepcopy(old_list)
Очевидно, самый медленный и самый необходимый для памяти способ, но иногда неизбежный.
Пример:
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return str(self.val)
foo = Foo(1)
a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)
# edit orignal list and instance
a.append('baz')
foo.val = 5
print('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
% (a, b, c, d, e, f))
Результат:
original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]
Феликс уже дал отличный ответ, но я думал, что сделаю сравнение скорости различных методов:
copy.deepcopy(old_list)
Copy()
, копирующий классы с помощью deepcopyCopy()
не копирует классы (только dicts/lists/tuples)for item in old_list: new_list.append(item)
[i for i in old_list]
(a понимание списка)copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
(список фрагментов)Таким образом, самая быстрая сортировка списка. Но имейте в виду, что copy.copy()
, list[:]
и list(list)
, в отличие от copy.deepcopy()
, а версия python не копирует списки, словари и экземпляры класса в списке, поэтому, если оригиналы меняются, они будут меняться в скопированный список тоже и наоборот.
(Здесь script, если кто-либо заинтересован или хочет поднять любые проблемы:)
from copy import deepcopy
class old_class:
def __init__(self):
self.blah = 'blah'
class new_class(object):
def __init__(self):
self.blah = 'blah'
dignore = {str: None, unicode: None, int: None, type(None): None}
def Copy(obj, use_deepcopy=True):
t = type(obj)
if t in (list, tuple):
if t == tuple:
# Convert to a list if a tuple to
# allow assigning to when copying
is_tuple = True
obj = list(obj)
else:
# Otherwise just do a quick slice copy
obj = obj[:]
is_tuple = False
# Copy each item recursively
for x in xrange(len(obj)):
if type(obj[x]) in dignore:
continue
obj[x] = Copy(obj[x], use_deepcopy)
if is_tuple:
# Convert back into a tuple again
obj = tuple(obj)
elif t == dict:
# Use the fast shallow dict copy() method and copy any
# values which aren't immutable (like lists, dicts etc)
obj = obj.copy()
for k in obj:
if type(obj[k]) in dignore:
continue
obj[k] = Copy(obj[k], use_deepcopy)
elif t in dignore:
# Numeric or string/unicode?
# It immutable, so ignore it!
pass
elif use_deepcopy:
obj = deepcopy(obj)
return obj
if __name__ == '__main__':
import copy
from time import time
num_times = 100000
L = [None, 'blah', 1, 543.4532,
['foo'], ('bar',), {'blah': 'blah'},
old_class(), new_class()]
t = time()
for i in xrange(num_times):
Copy(L)
print 'Custom Copy:', time()-t
t = time()
for i in xrange(num_times):
Copy(L, use_deepcopy=False)
print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t
t = time()
for i in xrange(num_times):
copy.copy(L)
print 'copy.copy:', time()-t
t = time()
for i in xrange(num_times):
copy.deepcopy(L)
print 'copy.deepcopy:', time()-t
t = time()
for i in xrange(num_times):
L[:]
print 'list slicing [:]:', time()-t
t = time()
for i in xrange(num_times):
list(L)
print 'list(L):', time()-t
t = time()
for i in xrange(num_times):
[i for i in L]
print 'list expression(L):', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(L)
print 'list extend:', time()-t
t = time()
for i in xrange(num_times):
a = []
for y in L:
a.append(y)
print 'list append:', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(i for i in L)
print 'generator expression extend:', time()-t
РЕДАКТИРОВАТЬ: добавлены классы старого стиля, стили старого стиля и диктофоны к эталонам, а также значительно упростили версию python и добавили еще несколько методов, включая выражения списков и extend()
.
timeit
. Кроме того, вы не можете сделать много выводов из произвольных микро тестов, как это.
Я сказал что Python 3.3+ добавляет list.copy()
, который должен быть таким же быстрым, как нарезка:
newlist = old_list.copy()
s.copy()
создает поверхностную копию s
(так же, как s[:]
).
Каковы варианты клонирования или копирования списка в Python?
В Python 3 мелкая копия может быть выполнена с помощью:
a_copy = a_list.copy()
В Python 2 и 3 вы можете получить мелкую копию с полным фрагментом оригинала:
a_copy = a_list[:]
Существует два семантических способа копирования списка. Неглубокая копия создает новый список тех же объектов, глубокая копия создает новый список, содержащий новые эквивалентные объекты.
Неглубокая копия копирует только сам список, который является контейнером ссылок на объекты в списке. Если объекты, содержащиеся в них, являются изменяемыми и один из них изменен, это изменение будет отражено в обоих списках.
В Python 2 и 3. существуют разные способы сделать это. Пути Python 2 также будут работать в Python 3.
В Python 2 идиоматический способ создания мелкой копии списка состоит из полного фрагмента оригинала:
a_copy = a_list[:]
Вы также можете выполнить одно и то же, передав список через конструктор списка,
a_copy = list(a_list)
но использование конструктора менее эффективно:
>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
В Python 3 списки получают метод list.copy
:
a_copy = a_list.copy()
В Python 3.5:
>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125
Использование new_list = my_list затем изменяет new_list каждый раз, когда изменяется my_list. Почему это?
my_list
- это просто имя, которое указывает на фактический список в памяти. Когда вы говорите new_list = my_list
, вы не делаете копию, вы просто добавляете другое имя, указывающее на этот исходный список в памяти. У нас могут быть подобные проблемы, когда мы делаем копии списков.
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
Список - это всего лишь массив указателей на содержимое, поэтому мелкая копия просто копирует указатели, поэтому у вас есть два разных списка, но они имеют одинаковое содержимое. Чтобы сделать копии содержимого, вам нужна глубокая копия.
Чтобы сделать глубокую копию списка в Python 2 или 3, используйте deepcopy
в модуле copy
:
import copy
a_deep_copy = copy.deepcopy(a_list)
Чтобы продемонстрировать, как это позволяет создавать новые под-списки:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
Итак, мы видим, что глубокий скопированный список - это совсем другой список из оригинала. Вы можете перевернуть свою собственную функцию, но не надо. Вероятно, вы создадите ошибки, которые в противном случае не были бы доступны, используя функцию стандартной копии библиотеки.
eval
Вы можете видеть, что это используется как способ глубокой печати, но не делайте этого:
problematic_deep_copy = eval(repr(a_list))
В 64-битном Python 2.7:
>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206
на 64-битном Python 3.5:
>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
list_copy=[]
for item in list: list_copy.append(copy(item))
и это намного быстрее.
Уже есть много ответов, которые расскажут вам, как сделать правильную копию, но никто из них не говорит, почему ваша оригинальная "копия" не удалась.
Python не сохраняет значения в переменных; он связывает имена с объектами. Ваше исходное задание заняло объект, на который ссылается my_list
и связал его с new_list
. Независимо от того, какое имя вы используете, остается только один список, поэтому изменения, сделанные при обращении к нему как my_list
будут сохраняться при обращении к нему как new_list
. Каждый из других ответов на этот вопрос дает вам различные способы создания нового объекта для привязки к new_list
.
Каждый элемент списка действует как имя, поскольку каждый элемент связывается не исключительно с объектом. Неглубокая копия создает новый список, элементы которого привязываются к тем же объектам, что и раньше.
new_list = list(my_list) # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]
Чтобы сделать копию списка еще одним шагом, скопируйте каждый объект, к которому относится ваш список, и привяжите эти копии элементов к новому списку.
import copy
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Это еще не глубокая копия, потому что каждый элемент списка может ссылаться на другие объекты, точно так же, как список привязан к его элементам. Чтобы рекурсивно скопировать каждый элемент в списке, а затем каждый другой объект, на который ссылаются каждый элемент, и т. Д.: выполните глубокую копию.
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Дополнительную информацию об угловых случаях при копировании см. В документации.
new_list = list(old_list)
Используйте thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Идиома Python для этого - newList = oldList[:]
Все остальные участники предоставили ответы большие, которые работают, когда у вас есть один размерный (выровненный) список, однако из тех методов, о которых идет речь, только copy.deepcopy()
работает для клонирования/копирования и не указывать на вложенные объекты list
, когда вы работаете с многомерными вложенными списками (список списков). В то время как Феликс Клинг ссылается на это в своем ответе, есть немного больше об этой проблеме и, возможно, обходной путь с использованием встроенных модулей, которые могут оказаться более быстрой альтернативой deepcopy
.
Пока new_list = old_list[:]
, copy.copy(old_list)'
и для Py3k old_list.copy()
работают для одноуровневых списков, они возвращаются к объектам list
, вложенным в old_list
и new_list
, и изменяются на один объектов list
увековечены в другом.
Как было указано как Aaron Hall, и PM 2Ring с использованием
eval()
не только плохая идея, но и намного медленнее, чемcopy.deepcopy()
.Это означает, что для многомерных списков единственным вариантом является
copy.deepcopy()
. С учетом сказанного это действительно не вариант, так как производительность идет на юг, когда вы пытаетесь использовать его в многомерном массиве умеренного размера. Я попыталсяtimeit
использовать массив 42x42, не неслыханный или даже такой большой для приложений для биоинформатики, и я отказался от ожидания ответа и только начал вводить свое редактирование на этот пост.Казалось бы, единственным реальным вариантом является инициализация нескольких списков и работа над ними независимо. Если у кого-то есть другие предложения, для того, как обращаться с многомерным копированием списков, было бы полезно.
Как отмечали другие, являются значительными проблемами производительности с использованием модуля copy
и copy.deepcopy
для многомерных списков. Попытка разработать другой способ копирования многомерного списка без использования deepcopy
(я работал над проблемой для курса, который позволяет всего 5 секунд для запуска всего алгоритма для получения кредита), я придумал способ использования встроенных функций, чтобы сделать копию вложенного списка, не указывая друг на друга или на объекты list
, вложенные в них. Я использовал eval()
и repr()
в присваивании, чтобы сделать копию старого списка в новый список без создания ссылки на старый список. Он принимает форму:
new_list = eval(repr(old_list))
В основном, это делает представление old_list
в виде строки, а затем оценивает строку, как если бы это был объект, который представляет строка. Таким образом, не создается ссылка на исходный объект list
. Создается новый объект list
, и каждая переменная указывает на свой собственный независимый объект. Вот пример использования 2-мерного вложенного списка.
old_list = [[0 for j in range(y)] for i in range(x)] # initialize (x,y) nested list
# assign a copy of old_list to new list without them pointing to the same list object
new_list = eval(repr(old_list))
# make a change to new_list
for j in range(y):
for i in range(x):
new_list[i][j] += 1
Если вы затем проверите содержимое каждого списка, например, список 4 на 3, Python вернет
>>> new_list
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> old_list
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
Хотя это, вероятно, не канонический или синтаксически правильный способ сделать это, похоже, что он работает хорошо. Я не тестировал производительность, но я собираюсь предположить, что eval()
и rep()
будут иметь меньше накладных расходов, чем deepcopy
.Забастовкa >
repr()
, достаточно для воссоздания объекта. Кроме того, eval()
является инструментом последней инстанции; см. Эвал действительно опасен для SO таким ветераном Недом Батчелдером. Поэтому, когда вы пропагандируете использование eval()
вам действительно следует упомянуть, что это может быть опасно.
eval()
в Python в целом является риском. Дело не в том, используете ли вы функцию в коде, а в том, что это дыра в безопасности Python сама по себе. Мой пример не использует его с функцией, которая получает входные данные от input()
, sys.agrv
или даже текстового файла. Это больше похоже на инициализацию пустого многомерного списка один раз, а затем просто способ скопировать его в цикл вместо повторной инициализации на каждой итерации цикла.
В отличие от других языков, имеющих переменную и ценность, у Python есть имя и объект.
Это утверждение:
a = [1,2,3]
означает присвоить списку (объекту) имя a
, и это:
b = a
просто дает один и тот же объект a
новым именем b
, так что всякий раз, когда вы делаете что - то с, изменения объекта и, следовательно, a
b
изменений.
Единственный способ сделать копию на самом деле - создать новый объект, как и другие ответы, уже сказал.
Вы можете увидеть больше об этом здесь.
Начните с начала и исследователя немного глубоко:
Итак, у вас есть два списка:
list_1=['01','98']
list_2=[['01','98']]
И мы должны скопировать оба списка, начиная с первого списка:
Итак, сначала попробуйте общий метод копирования:
copy=list_1
Теперь, если вы думаете, что копия скопировала list_1, вы можете ошибаться, пусть это проверит:
The id() function shows us that both variables point to the same list object, i.e. they share this object.
print(id(copy))
print(id(list_1))
выход:
4329485320
4329485320
Удивлен? Хорошо, пусть исследует это:
Так как мы знаем, что python ничего не хранит в переменной, переменные просто ссылаются на объект и объект хранят значение. Здесь объект list
, но мы создали две ссылки на тот же объект двумя разными именами переменных. Поэтому обе переменные указывают на один и тот же объект:
поэтому, когда вы делаете copy=list_1
, что на самом деле его делает:
Здесь в списке image_1 и копировании находятся два имени переменной, но объект одинаковый для обеих переменных, который равен list
Поэтому, если вы попытаетесь изменить скопированный список, он также изменит исходный список, потому что список только один, вы измените этот список независимо от того, что вы делаете из скопированного списка или из исходного списка:
copy[0]="modify"
print(copy)
print(list_1)
выход:
['modify', '98']
['modify', '98']
Поэтому он изменил исходный список:
Что такое решение?
Решение:
Теперь перейдите ко второму питоническому методу копирования списка:
copy_1=list_1[:]
Теперь этот метод исправить то, с чем мы столкнулись в первом выпуске, проверить его:
print(id(copy_1))
print(id(list_1))
4338792136
4338791432
Итак, поскольку мы можем видеть, что в нашем списке есть другой идентификатор, и это означает, что обе переменные указывают на разные объекты, так что на самом деле происходит следующее:
Теперь попробуйте изменить список и посмотрим, остаемся ли мы перед предыдущей проблемой:
copy_1[0]="modify"
print(list_1)
print(copy_1)
Вывод:
['01', '98']
['modify', '98']
Итак, вы можете видеть, что он не изменяет исходный список, он только модифицировал скопированный список, поэтому мы в порядке с ним.
Итак, теперь я думаю, что мы закончили? подождите, мы также должны скопировать второй вложенный список, чтобы попробовать попробовать pythonic:
copy_2=list_2[:]
Итак, list_2 должен ссылаться на другой объект, который является копией list_2, чтобы проверить:
print(id((list_2)),id(copy_2))
получаем результат:
4330403592 4330403528
Теперь мы можем предположить, что оба списка указывают на другой объект, поэтому теперь попробуйте изменить его и увидеть, что он дает то, что мы хотим:
Итак, когда мы пытаемся:
copy_2[0][1]="modify"
print(list_2,copy_2)
он дает нам вывод:
[['01', 'modify']] [['01', 'modify']]
Теперь, это немного запутанно, мы использовали питоновский путь, и все же мы сталкиваемся с той же проблемой.
понять это:
Итак, когда мы делаем:
copy_2=list_2[:]
мы фактически копируем только внешний список, а не вложенный список, поэтому вложенный список является одним и тем же объектом для обоих списков, пусть проверяет:
print(id(copy_2[0]))
print(id(list_2[0]))
выход:
4329485832
4329485832
Итак, когда мы делаем copy_2=list_2[:]
, это происходит:
Он создает копию списка, но только внешняя копия списка, а не вложенная копия списка, вложенный список одинаковый для обеих переменных, поэтому, если вы попытаетесь изменить вложенный список, тогда он также изменит исходный список, потому что вложенный объект списка для обоих вложенных списков.
Итак, каково решение?
Решение deep copy
from copy import deepcopy
deep=deepcopy(list_2)
Итак, теперь проверьте:
print(id((list_2)),id(deep))
выход:
4322146056 4322148040
оба идентификатора разные, теперь пусть проверяет идентификатор вложенного списка:
print(id(deep[0]))
print(id(list_2[0]))
выход:
4322145992
4322145800
Как вы можете видеть, оба идентификатора различны, поэтому мы можем предположить, что оба вложенных списка теперь указывают на другой объект.
Итак, когда вы делаете deep=deepcopy(list_2)
, что на самом деле происходит:
Таким образом, оба вложенных списка указывают на другой объект, и теперь они имеют отдельную копию вложенного списка.
Теперь попробуйте изменить вложенный список и посмотрим, разрешила ли он предыдущую проблему или нет:
так что если мы делаем:
deep[0][1]="modify"
print(list_2,deep)
выход:
[['01', '98']] [['01', 'modify']]
Итак, как вы видите, он не изменил первоначальный вложенный список, он только изменил скопированный список.
Если вам понравился мой подробный ответ, сообщите мне, повысив его, если у вас есть какие-либо сомнения в этом ответе, прокомментируйте:)
Ниже приведены результаты синхронизации с использованием Python 3.6.0. Имейте в виду, что эти времена относительно друг друга, а не абсолютные.
Я придерживался только мелких копий, а также добавил некоторые новые методы, которые не были возможны в Python2, например list.copy()
(эквивалент среза Python3 ) и распаковать список (*new_list, = list
):
METHOD TIME TAKEN
b = a[:] 6.468942025996512 #Python2 winner
b = a.copy() 6.986593422974693 #Python3 "slice equivalent"
b = []; b.extend(a) 7.309216841997113
b = a[0:len(a)] 10.916740721993847
*b, = a 11.046738261007704
b = list(a) 11.761539687984623
b = [i for i in a] 24.66165203397395
b = copy.copy(a) 30.853400873980718
b = []
for item in a:
b.append(item) 48.19176080400939
Мы видим, что старый победитель по-прежнему выходит на первое место, но не на самом деле огромным количеством, учитывая повышенную читабельность подхода Python3 list.copy()
.
Обратите внимание, что эти методы не выдают эквивалентные результаты для любого ввода, отличного от списков. Все они работают для разрезаемых объектов, несколько работают для любого итерабельного, но только copy.copy()
работает для любого объекта Python.
Вот код тестирования для заинтересованных сторон (Шаблон отсюда):
import timeit
COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'
print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []\nfor item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a: b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
list
в Python 3.6.1. У меня нет установки Python 3.6.0, но b = list(a)
получает 2.7
и b = a[:]
получает 3.1
, а b = a.copy()
получает 3.1
(на моих Windows и Linux с CPython 3.6 .1), поэтому list()
примерно на 10% быстрее
Меня удивляет, что это еще не упоминалось, поэтому ради полноты...
Вы можете выполнить распаковку списка с помощью оператора "splat": *
, который также скопирует элементы вашего списка.
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
Очевидным недостатком этого метода является то, что он доступен только в Python 3.5 +.
С учетом времени, похоже, это работает лучше, чем другие распространенные методы.
x = [random.random() for _ in range(1000)]
%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]
%timeit a = [*x]
#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
old_list
и new_list
- это два разных списка, редактирование одного не изменит другого (если вы непосредственно не изменили сами элементы (например, список списков), ни один из этих методов не является глубоким копированием).
Не уверен, что это все еще актуально, но то же самое относится и к словарям. Посмотрите на этот пример.
a = {'par' : [1,21,3], 'sar' : [5,6,8]}
b = a
c = a.copy()
a['har'] = [1,2,3]
a
Out[14]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]}
b
Out[15]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]}
c
Out[16]: {'par': [1, 21, 3], 'sar': [5, 6, 8]}
Обратите внимание, что в некоторых случаях, если вы определили свой собственный пользовательский класс и хотите сохранить атрибуты, вы должны использовать copy.copy()
или copy.deepcopy()
а не альтернативы, например, в Python 3:
import copy
class MyList(list):
pass
lst = MyList([1,2,3])
lst.name = 'custom list'
d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}
for k,v in d.items():
print('lst: {}'.format(k), end=', ')
try:
name = v.name
except AttributeError:
name = 'NA'
print('name: {}'.format(name))
Выходы:
lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
вы можете использовать функцию bulit в списке():
newlist=list(oldlist)
Я думаю, этот код поможет вам.
Очень простой подход, не зависящий от версии python, отсутствовал в уже заданных ответах, которые вы можете использовать большую часть времени (по крайней мере, я):
new_list = my_list * 1 #Solution 1 when you are not using nested lists
Однако, если my_list содержит другие контейнеры (например, вложенные списки), вы должны использовать deepcopy, как другие, предложенные в ответах выше из библиотеки копий. Например:
import copy
new_list = copy.deepcopy(my_list) #Solution 2 when you are using nested lists
. Бонус. Если вы не хотите копировать элементы (ака мелкой копии):
new_list = my_list[:]
Позвольте понять разницу между решением № 1 и решением № 2
>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])
Как вы можете видеть, решение №1 отлично работало, когда мы не использовали вложенные списки. Проверьте, что произойдет, когда мы применим решение №1 к вложенным спискам.
>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]] #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] #Solution #2 - DeepCopy worked in nested list
new_list = my_list[:]
new_list = my_list
Попытайтесь это понять. Скажем, что my_list находится в кучевой памяти в местоположении X, то есть my_list указывает на X. Теперь, назначив new_list = my_list
, вы укажете new_list, указывающий на X. Это называется мелкой копией.
Теперь, если вы назначили new_list = my_list[:]
, вы просто копируете каждый объект my_list в new_list. Это называется Deep copy.
Другой способ, которым вы можете это сделать:
new_list = list(old_list)
import copy
new_list = copy.deepcopy(old_list)
Вам понравится использовать deepcopy из стандартной библиотеки python.
В python, когда вы копируете тип данных, исходный и скопированные типы данных имеют одинаковые расположения в памяти. Следовательно, любые изменения, внесенные в копию объекта, отражаются в исходном объекте. Например, рассмотрим следующее:
my_lst=[1,2,3,4,5] #Python list
print my_lst, ' my_lst (before copy)'
my_lst_copy = my_lst=[1,2,3,4,5] #Simple copy of python list
my_lst_copy[2] = 55 #Copy of python list changed
print my_lst_copy, ' my_lst_copy (copy of python list)'
print my_lst, ' my_lst (after copy)'
>>>[1, 2, 3, 4, 5] my_lst (before copy)
>>>[1, 2, 55, 4, 5] my_lst_copy (copy of python list)
>>>[1, 2, 55, 4, 5] my_lst (after copy)
Как вы уже заметили, и как вы еще раз заметили в приведенном выше примере, измените любой элемент скопированного списка. my_list_cp
изменяет исходный список my_list
. Причина этого заключается в том, что не было нового назначения для my_list_cp
.
Вы можете противодействовать вышеуказанному, используя deepcopy из стандартной библиотеки python. В глубокой копии копия объекта копируется в другой объект.
from copy import deepcopy
my_lst=[1,2,3,4,5] #Python list
print my_lst, ' my_lst (before copy)'
my_lst_copy = deepcopy(my_lst) #Python list copied
my_lst_copy[2] = 55 #Copy of python list changed
print my_lst_copy, ' my_lst_copy (copy of python list)'
print my_lst, ' my_lst (after copy)'
>>>[1, 2, 3, 4, 5] my_lst (before copy)
>>>[1, 2, 55, 4, 5] my_lst_copy (copy of python list)
>>>[1, 2, 3, 4, 5] my_lst (after copy)
В приведенном выше примере вы видите, что my_lst
не изменился после копирования.
Другой метод (который, как мне кажется, достаточно читабельен) заключается в том, чтобы превратить его в строку, а затем переключить обратно в список.
new_list = list(''.join(my_list))
newlist = [*mylist]
также возможен в Python 3.newlist = list(mylist)
может быть более понятным, хотя.