Как клонировать или скопировать список?

Какие есть варианты для клонирования или копирования списка в Python?

При использовании new_list = my_listлюбые модификации new_list изменения my_list каждый раз. Почему это?

31 ответ

Решение

С 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)
    
  • Вы можете использовать общий 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]

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

  1. 10,59 с (105,9us / itn) - copy.deepcopy(old_list)
  2. 10,16 с (101,6us/itn) - чистый питон Copy() метод копирования классов с глубокой копией
  3. 1.488 сек (14.88us/itn) - чистый питон Copy() метод, не копирующий классы (только dicts / lists / tuples)
  4. 0,325 с (3,25 мкс / миль) - for item in old_list: new_list.append(item)
  5. 0,217 с (2,17us / itn) - [i for i in old_list] ( понимание списка)
  6. 0,186 с (1,86US / ITN) - copy.copy(old_list)
  7. 0,075 с (0,75us / itn) - list(old_list)
  8. 0,053 с (0,53us / itn) - new_list = []; new_list.extend(old_list)
  9. 0,039 с (0,39us / itn) - old_list[:] ( нарезка списка)

Так что самым быстрым является нарезка списка. Но знайте, что copy.copy(), list[:] а также list(list), В отличие от copy.deepcopy() и версия Python не копирует никакие списки, словари и экземпляры классов в списке, поэтому, если оригиналы изменятся, они также изменятся и в скопированном списке, и наоборот.

(Вот сценарий, если кто-то заинтересован или хочет поднять какие-либо вопросы:)

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's 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(),

Мне сказали, что Python 3.3+ добавляетlist.copy() метод, который должен быть таким же быстрым, как нарезка:

newlist = old_list.copy()

Какие есть варианты для клонирования или копирования списка в Python?

В Python 3 поверхностная копия может быть сделана с помощью:

a_copy = a_list.copy()

В Python 2 и 3 вы можете получить поверхностную копию с полным фрагментом оригинала:

a_copy = a_list[:]

объяснение

Есть два семантических способа скопировать список. Малая копия создает новый список тех же объектов, а глубокая копия создает новый список, содержащий новые эквивалентные объекты.

Мелкая копия списка

Мелкая копия копирует только сам список, который является контейнером ссылок на объекты в списке. Если содержащиеся в нем объекты являются изменяемыми и один из них изменяется, это изменение будет отражено в обоих списках.

Есть разные способы сделать это в Python 2 и 3. Пути Python 2 также будут работать в Python 3.

Python 2

В 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

В 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'], [], []]

Итак, мы видим, что глубоко скопированный список - это совершенно другой список от оригинала. Вы можете свернуть свою собственную функцию - но не делайте этого. Скорее всего, вы будете создавать ошибки, которых не было бы, используя функцию Deepcopy стандартной библиотеки.

Не использовать eval

Вы можете увидеть это как способ глубокой копии, но не делайте этого:

problematic_deep_copy = eval(repr(a_list))
  1. Это опасно, особенно если вы оцениваете что-то из источника, которому не доверяете.
  2. Это ненадежно, если копируемый подэлемент не имеет представления, которое может быть вычислено для воспроизведения эквивалентного элемента.
  3. Это также менее производительно.

В 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_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 что на самом деле делает:

Здесь на изображении list_1 и copy два имени переменных, но объект одинаков для обеих переменных, которые 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']

Итак, как вы можете видеть, он не изменяет исходный список, он только изменяет скопированный список, так что мы в порядке.

Так что теперь я думаю, что мы закончили? подождите, мы тоже должны скопировать второй вложенный список, так что давайте попробуем питонски:

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 не хранит значения в переменных; это связывает имена с объектами. Ваше первоначальное назначение заняло объект, указанный 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)

См. Документацию для получения дополнительной информации об угловых случаях при копировании.

Использование thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

Сроки Python 3.6.0

Вот результаты синхронизации с использованием 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))

Идиома Python для этого newList = oldList[:]

new_list = list(old_list)

Все остальные участники дали отличные ответы, которые работают, когда у вас есть одноуровневый (выровненный) список, однако из методов, упомянутых до сих пор, только copy.deepcopy() работает, чтобы клонировать / скопировать список и не указывать на вложенный list объекты при работе с многомерными вложенными списками (list of lists). Хотя Felix Kling ссылается на это в своем ответе, есть еще немного проблемы, и, возможно, обходной путь, использующий встроенные модули, которые могут оказаться более быстрой альтернативой 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 объект создан, и каждая переменная указывает на свой собственный независимый объект. Вот пример использования двухмерного вложенного списка.

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 будут.

Меня удивляет, что это еще не было упомянуто, поэтому ради полноты...

Вы можете выполнить распаковку списка с помощью "оператора сплат": *, который также будет копировать элементы вашего списка.

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)

В отличие от других языков, которые имеют переменную и значение, Python имеет имя и объект.

Это утверждение:

a = [1,2,3]

означает дать списку (объекту) имя a, и это:

b = a

просто дает тот же объект a новое имя bтак что всякий раз, когда вы делаете что-то с aобъект меняется и, следовательно, b изменения.

Единственный способ сделать действительно копию - это создать новый объект, как уже говорили другие ответы.

Вы можете увидеть больше об этом здесь.

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. Это известно как Глубокая копия.

Другой способ сделать это:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)

Очень простой подход, независимый от версии 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

Я хотел опубликовать что-то немного другое, чем некоторые другие ответы. Хотя это, скорее всего, не самый понятный или самый быстрый вариант, он дает некоторое представление о том, как работает глубокое копирование, а также является еще одним альтернативным вариантом для глубокого копирования. На самом деле не имеет значения, есть ли в моей функции ошибки, поскольку цель этого - показать способ копирования объектов, таких как ответы на вопросы, но также использовать это как точку, чтобы объяснить, как deepcopy работает в своей основе.

В основе любой функции глубокого копирования лежит способ создания неглубокой копии. Как? Просто. Любая функция глубокого копирования дублирует только контейнеры неизменяемых объектов. Когда вы глубоко копируете вложенный список, вы дублируете только внешние списки, а не изменяемые объекты внутри списков. Вы только дублируете контейнеры. То же самое и с классами. Когда вы глубоко копируете класс, вы глубоко копируете все его изменяемые атрибуты. Так как? Почему вам нужно скопировать только контейнеры, такие как списки, словари, кортежи, итеры, классы и экземпляры классов?

Это просто. Изменяемый объект не может быть продублирован. Его нельзя изменить, поэтому это только одно значение. Это означает, что вам никогда не придется дублировать строки, числа, логические значения или любые из них. Но как бы вы продублировали контейнеры? Просто. Вы просто инициализируете новый контейнер со всеми значениями. Deepcopy полагается на рекурсию. Он дублирует все контейнеры, даже те, в которых есть контейнеры, до тех пор, пока контейнеров не останется. Контейнер - это неизменный объект.

Как только вы это узнаете, полностью дублировать объект без каких-либо ссылок довольно просто. Вот функция для глубокого копирования основных типов данных (не будет работать для пользовательских классов, но вы всегда можете добавить это)

def deepcopy(x):
  immutables = (str, int, bool, float)
  mutables = (list, dict, tuple)
  if isinstance(x, immutables):
    return x
  elif isinstance(x, mutables):
    if isinstance(x, tuple):
      return tuple(deepcopy(list(x)))
    elif isinstance(x, list):
      return [deepcopy(y) for y in x]
    elif isinstance(x, dict):
      values = [deepcopy(y) for y in list(x.values())]
      keys = list(x.keys())
      return dict(zip(keys, values))

Собственный встроенный Deepcopy Python основан на этом примере. Единственное отличие состоит в том, что он поддерживает другие типы, а также поддерживает пользовательские классы путем дублирования атрибутов в новый повторяющийся класс, а также блокирует бесконечную рекурсию со ссылкой на объект, который он уже видел, используя список заметок или словарь. И это действительно все для создания глубоких копий. По сути, создание глубокой копии - это просто создание мелких копий. Надеюсь, этот ответ кое-что добавит к вопросу.

ПРИМЕРЫ

Допустим, у вас есть этот список: [1, 2, 3]. Неизменяемые числа не могут быть продублированы, но другой слой может. Вы можете продублировать его, используя понимание списка: [x вместо x in [1, 2, 3]

Теперь представьте, что у вас есть этот список: [[1, 2], [3, 4], [5, 6]]. На этот раз вы хотите создать функцию, которая использует рекурсию для глубокого копирования всех слоев списка. Вместо понимания предыдущего списка:

[x for x in _list]

Для списков используется новый:

[deepcopy_list(x) for x in _list]

И deepcopy_list выглядит так:

def deepcopy_list(x):
  if isinstance(x, (str, bool, float, int)):
    return x
  else:
    return [deepcopy_list(y) for y in x]

Теперь у вас есть функция, которая может копировать любой список strs, bools, floast, int и даже списки на бесконечное количество слоев с использованием рекурсии. И вот оно, глубокое копирование.

TLDR: DeepCopy использует рекурсию дублировать объекты, а просто возвращает те же неизменные объекты, как раньше, так как неизменные объекты не могут быть продублированы. Однако он глубоко копирует самые внутренние слои изменяемых объектов, пока не достигнет самого внешнего изменяемого слоя объекта.

Обратите внимание, что в некоторых случаях, если вы определили собственный класс и хотите сохранить атрибуты, вам следует использовать 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

Помните, что в Python, когда вы делаете:

    list1 = ['apples','bananas','pineapples']
    list2 = list1

List2 хранит не фактический список, а ссылку на list1. Поэтому, когда вы делаете что-либо для list1, list2 также изменяется. используйте модуль копирования (не по умолчанию, загрузка по пипу), чтобы сделать исходную копию списка (copy.copy() для простых списков, copy.deepcopy()для вложенных). Это делает копию, которая не меняется с первым списком.

Не уверен, что это все еще актуально, но то же самое относится и к словарям. Посмотрите на этот пример.

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]}

Вы можете использовать bulit в функции list():

newlist=list(oldlist)

Я думаю, что этот код поможет вам.

Небольшая практическая перспектива заглянуть в память через id и gc.

>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']

>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272) 
     |           |
      -----------

>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
     |           |           |
      -----------------------

>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
     |           |           |
      -----------------------

>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word'])  # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
     |           |
      -----------

>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
     |           |           |
      -----------------------

>>> import gc
>>> gc.get_referrers(a[0]) 
[['hell', 'word'], ['hell', 'word']]  # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None) 

Это потому, что строка new_list = my_list назначает новую ссылку на переменную my_list который new_listЭто похоже на C код, приведенный ниже,

int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;

Вы должны использовать модуль копирования для создания нового списка

import copy
new_list = copy.deepcopy(my_list)

Есть еще один способ скопировать список, которого до сих пор не было в списке: добавление пустого списка: l2 = l + []. Я тестировал его на Python 3.8:

l = [1,2,3]
l2 = l + []
print(l,l2)
l[0] = 'a'
print(l,l2)

Не лучший ответ, но он работает.

Используемый метод зависит от содержимого копируемого списка. Если список содержит вложенные dicts чем Deepcopy - единственный метод, который работает, в противном случае большинство методов, перечисленных в ответах (slice, loop [for], copy, extension, Объединение или распаковка), будут работать и выполняться в одно и то же время (за исключением цикла и deepcopy, которые предстояло худшее).

Сценарий

      from random import randint
from time import time
import copy

item_count = 100000

def copy_type(l1: list, l2: list):
  if l1 == l2:
    return 'shallow'
  return 'deep'

def run_time(start, end):
  run = end - start
  return int(run * 1000000)

def list_combine(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = [] + l1
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'combine', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_extend(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = []
  l2.extend(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'extend', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_unpack(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = [*l1]
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'unpack', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_deepcopy(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = copy.deepcopy(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'deepcopy', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_copy(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = list.copy(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'copy', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_slice(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = l1[:]
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'slice', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_loop(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = []
  for i in range(len(l1)):
    l2.append(l1[i])
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'loop', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_list(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = list(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'list()', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

if __name__ == '__main__':
  list_type = [{'list[dict]': {'test': [1, 1]}}, 
          {'list[list]': [1, 1]}]
  store = []
  for data in list_type:
    key = list(data.keys())[0]
    store.append({key: [list_unpack(data[key]), list_extend(data[key]), 
                list_combine(data[key]), list_deepcopy(data[key]), 
                list_copy(data[key]), list_slice(data[key]),           
                list_loop(data[key])]})
  print(store)

Полученные результаты

      [{"list[dict]": [
  {"method": "unpack", "copy_type": "shallow", "time_µs": 56149},
  {"method": "extend", "copy_type": "shallow", "time_µs": 52991},
  {"method": "combine", "copy_type": "shallow", "time_µs": 53726},
  {"method": "deepcopy", "copy_type": "deep", "time_µs": 2702616},
  {"method": "copy", "copy_type": "shallow", "time_µs": 52204},
  {"method": "slice", "copy_type": "shallow", "time_µs": 52223},
  {"method": "loop", "copy_type": "shallow", "time_µs": 836928}]},
{"list[list]": [
  {"method": "unpack", "copy_type": "deep", "time_µs": 52313},
  {"method": "extend", "copy_type": "deep", "time_µs": 52550},
  {"method": "combine", "copy_type": "deep", "time_µs": 53203},
  {"method": "deepcopy", "copy_type": "deep", "time_µs": 2608560},
  {"method": "copy", "copy_type": "deep", "time_µs": 53210},
  {"method": "slice", "copy_type": "deep", "time_µs": 52937},
  {"method": "loop", "copy_type": "deep", "time_µs": 834774}
]}]

Опция deepcopy - единственный метод, который мне подходит:

from copy import deepcopy

a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')

приводит к выходу:

Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------

Есть простой способ справиться с этим.

Код:

number=[1,2,3,4,5,6] #Original list
another=[] #another empty list
for a in number: #here I am declaring variable (a) as an item in the list (number)
    another.append(a) #here we are adding the items of list (number) to list (another)
print(another)

Выход:

>>> [1,2,3,4,5,6]

Надеюсь, это было полезно для вашего запроса.

Вам понравится использовать 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 не изменился после копирования.

Списки Python - это изменяемые объекты.

m = list([1, 2, 3])`
n = m

a = 5
b = a

id(a) == id(b)`
# id() return "identity" of the object.
# True,  Both a and b are references to the same object. 
id(m) == id(n)
# True,  Both m and n are references to the same object. 

b = b + 2
id(a) == id(b)
# False, a new object on separate location is created, which b will point.

n.pop()
id(m) == id(n)
# True, the object is mutated, and m and n still references to the same object.

Поскольку списки Python изменяемы, m и n по-прежнему будут ссылаться на один и тот же объект после мутации. В то время как для неизменяемых объектов, таких как int, будет создан новый объект, а идентификатор будет ссылаться на новый объект.

Суть в том, что в вашем сценарии был только один объект, поскольку списки Python изменяемы.

Однако, если вам нужно, чтобы исходный список оставался неизменным при изменении нового списка, вы можете использовать метод copy().

new_list = original_list.copy()

Идентификаторы new_list а также original_list отличается.

Подробнее о изменчивости можно узнать здесь: https://medium.com/@meghamohan/mutable-and-immutable-side-of-python-c2145cf72747.

Краткое и простое объяснение каждого режима копирования:

Неглубокая копия создает новый составной объект, а затем (насколько это возможно) вставляет в него ссылки на объекты, найденные в оригинале, создавая поверхностную копию:

      new_list = my_list

Глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале, создавая глубокую копию:

      new_list = list(my_list)

list()отлично работает для глубокой копии простых списков, например:

      my_list = ["A","B","C"]

Но для сложных списков, таких как...

      my_complex_list = [{'A' : 500, 'B' : 501},{'C' : 502}]

...использоватьdeepcopy():

      import copy
new_complex_list = copy.deepcopy(my_complex_list)
      new_list = my_list

потому что: new_list будет только ссылкой на my_list , и изменения, сделанные в new_list , будут автоматически также сделаны в my_list и наоборот

Есть два простых способа скопировать список

      new_list = my_list.copy()

или

      new_list = list(my_list)
Другие вопросы по тегам