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

Оператор == на списках и dicts не сравнивает идентификаторы объектов, чтобы увидеть, являются ли они одним и тем же объектом. Для этого используйте obj1 is obj2.

Вместо этого оператор == сравнивает членов списка из dict, чтобы узнать, совпадают ли они.

2224
задан Satya 22 February 2019 в 22:43
поделиться

5 ответов

С 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) { {1}} 

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

Пример:

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]
3027
ответ дан 22 November 2019 в 19:55
поделиться

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

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

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

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

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

Тогда теперь у Вас есть функция, которая может deepcopy любой список strs, bools, плавания, ints и даже списки к бесконечно многим слоям с помощью рекурсии. И там у Вас есть он, deepcopying.

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

0
ответ дан 22 November 2019 в 19:55
поделиться

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

  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) - для элемента в old_list: new_list.append (item)
  5. 0,217 сек (2,17us / itn) - [i вместо i в old_list] (понимание списка )
  6. 0,186 секунды (1,86us / itn) - copy.copy (old_list)
  7. 0,075 сек (0,75us / itn) - список (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
557
ответ дан 22 November 2019 в 19:55
поделиться

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

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
34
ответ дан 22 November 2019 в 19:55
поделиться

В Python для этого используется идиома newList = oldList[:]

32
ответ дан 22 November 2019 в 19:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: