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

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

2987
aF. 10 Апр 2010 в 12:49

16 ответов

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

  1. 10,59 с (105,9us / itn) - copy.deepcopy(old_list)
  2. 10,16 с (101,6us / itn) - чистый метод python Copy(), копирующий классы с помощью глубокой копии
  3. 1.488 сек (14.88us / itn) - метод чистого Python Copy(), не копирующий классы (только dicts / lists / tuples)
  4. 0,325 с (3,25us / itn) - 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
580
Georgy 12 Май 2019 в 14:16

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

Какие есть варианты для клонирования или копирования списка в 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
124
Aaron Hall 9 Янв 2018 в 15:33

Все остальные участники дали отличные ответы, которые работают, когда у вас есть одноуровневый (выровненный) список, однако из упомянутых выше методов только copy.deepcopy() работает для клонирования / копирования списка. и не указывать на вложенные list объекты при работе с многомерными вложенными списками (list of lists). Хотя Феликс Клинг ссылается на это в своем ответе, в этом вопросе есть немного больше и, возможно, обходной путь с помощью встроенные модули, которые могут оказаться более быстрой альтернативой deepcopy.

Хотя new_list = old_list[:], copy.copy(old_list)' и Py3k old_list.copy() работают для одноуровневых списков, они возвращаются к указанию на list объекты, вложенные в old_list и { {X5}}, а изменения одного из list объектов сохраняются в другом.

Редактировать: новая информация, представленная на свет

Как было указано Аароном Холлом и PM 2Ring с использованием eval() - не только плохая идея, но и намного медленнее, чем copy.deepcopy().

Это означает, что для многомерных списков единственным вариантом является copy.deepcopy(). С учетом вышесказанного, это действительно не вариант, поскольку производительность снижается, когда вы пытаетесь использовать ее в многомерном массиве умеренного размера. Я попытался timeit использовать массив размером 42x42, что было неслыханно или даже слишком много для приложений биоинформатики, и я разочаровался в ожидании ответа и просто начал печатать мое редактирование этого поста.

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

Как уже говорили другие, есть значительные проблемы с производительностью, использующие модуль copy и copy.deepcopy для многомерных списков .

18
Yvette Colomb 13 Май 2019 в 04:09

Давайте начнем с самого начала и исследуем этот вопрос.

Итак, предположим, у вас есть два списка:

list_1=['01','98']
list_2=[['01','98']]

И мы должны скопировать оба списка, теперь начиная с первого списка:

Итак, сначала давайте попробуем установить переменную copy в наш исходный список list_1:

copy=list_1

Теперь, если вы думаете, скопировать скопированный список_1, то вы не правы. Функция id может показать нам, если две переменные могут указывать на один и тот же объект. Давайте попробуем это:

print(id(copy))
print(id(list_1))

Результат:

4329485320
4329485320

Обе переменные являются одним и тем же аргументом. Вы удивлены?

Так как мы знаем, что python ничего не хранит в переменной, переменные просто ссылаются на объект, а объект хранит значение. Здесь объект является list, но мы создали две ссылки на этот же объект с двумя разными именами переменных. Это означает, что обе переменные указывают на один и тот же объект, только с разными именами.

Когда вы делаете copy=list_1, он на самом деле делает:

enter image description here

Здесь на изображении 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

Таким образом, мы видим, что оба списка имеют разные идентификаторы, и это означает, что обе переменные указывают на разные объекты. Так что на самом деле здесь происходит:

enter image description here

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

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[:]

Вы копируете только внешний список, а не внутренний. Мы можем использовать функцию id еще раз, чтобы проверить это.

print(id(copy_2[0]))
print(id(list_2[0]))

Результат:

4329485832
4329485832

Когда мы делаем copy_2=list_2[:], это происходит:

enter image description here

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

Каково решение? Решением является функция deepcopy.

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), что на самом деле происходит:

enter image description here

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

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

deep[0][1]="modify"
print(list_2,deep)

Он выводит:

[['01', '98']] [['01', 'modify']]

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

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

27
Corman 7 Фев 2020 в 22:24

Используйте thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
36
Paul Tarjan 10 Апр 2010 в 08:53

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

32
erisco 10 Апр 2010 в 08:53

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

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

Это просто. Изменчивый объект не может быть дублирован. Его нельзя изменить, поэтому это только одно значение. Это означает, что вам никогда не придется дублировать строки, числа, bools или любые другие. Но как бы вы продублировали контейнеры? Просто. Вы просто инициализируете новый контейнер со всеми значениями. 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))

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

ПРИМЕРЫ

Скажем, у вас есть этот список: [1, 2, 3] . Неизменяемые числа не могут быть дублированы, но другой слой может. Вы можете продублировать его, используя понимание списка: [x для x в [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, ints и даже списков до бесконечного числа слоев с помощью рекурсии. И вот, у вас это есть, глубокий копирование.

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

1
Corman 8 Сен 2019 в 02:25

Небольшая практическая перспектива заглянуть в память через 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) 
1
B.Mr.W. 23 Ноя 2019 в 19:01

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

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

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)
11
SCB 26 Фев 2018 в 02:33

Очень простой подход, независимый от версии 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
6
jainashish 1 Ноя 2017 в 08:08
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)
3
Ravi Shankar 26 Июн 2017 в 21:03

Сроки Python 3.6

Вот результаты синхронизации с использованием Python 3.6.8. Имейте в виду, что это время относительно друг друга, а не абсолютное.

Я придерживался только мелкого копирования, а также добавил некоторые новые методы, которые не были возможны в Python2, такие как list.copy() (Python3 эквивалентный фрагмент) и две формы распаковка списка (*new_list, = list и new_list = [*list]):

METHOD                  TIME TAKEN
b = [*a]                2.75180600000021
b = a * 1               3.50215399999990
b = a[:]                3.78278899999986  # Python2 winner (see above)
b = a.copy()            4.20556500000020  # Python3 "slice equivalent" (see above)
b = []; b.extend(a)     4.68069800000012
b = a[0:len(a)]         6.84498999999959
*b, = a                 7.54031799999984
b = list(a)             7.75815899999997
b = [i for i in a]      18.4886440000000
b = copy.copy(a)        18.8254879999999
b = []
for item in a:
  b.append(item)        35.4729199999997

Мы можем видеть, что победитель Python2 по-прежнему преуспевает, но не сильно вытесняет Python3 list.copy(), особенно учитывая превосходную читаемость последнего.

Темная лошадка - это метод распаковки и повторной упаковки (b = [*a]), который на ~ 25% быстрее, чем нарезка в сыром виде, и более чем в два раза быстрее, чем другой метод распаковки (*b, = a).

b = a * 1 также на удивление хорошо.

Обратите внимание, что эти методы не выводят эквивалентные результаты для любого ввода, кроме списков. Все они работают для срезаемых объектов, некоторые - для любых итеративных, но только {{X0} } работает для более общих объектов 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", 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\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\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 = []; for 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))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
29
River 14 Май 2019 в 02:54

Уже есть много ответов, которые говорят вам, как сделать правильную копию, но ни один из них не говорит, почему ваша оригинальная «копия» не удалась.

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)

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

52
jack 19 Янв 2018 в 16:00

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

newlist = old_list.copy()

145
River 5 Апр 2017 в 01:13

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

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

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

0
Dr. Hippo 22 Фев 2020 в 12:44