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

2016

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

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

Теги:
list
copy
clone

20 ответов

2740
Лучший ответ

С 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]
  • 0
    @FelixKling Я на 100% согласен. Для ответа на важный вопрос Python, этот вопрос немного разбросан и устарел.
  • 0
    Если я не ошибаюсь: newlist = [*mylist] также возможен в Python 3. newlist = list(mylist) может быть более понятным, хотя.
Показать ещё 7 комментариев
491

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

  • 10.59 сек (105.9us/itn) - copy.deepcopy(old_list)
  • 10.16 сек (101.6us/itn) - метод чистого питона Copy(), копирующий классы с помощью deepcopy
  • 1.488 сек (14.88us/itn) - чистый метод python Copy() не копирует классы (только dicts/lists/tuples)
  • 0,325 с (3,25us/itn) - for item in old_list: new_list.append(item)
  • 0.217 с (2.17us/itn) - [i for i in old_list] (a понимание списка)
  • 0.186 с (1.86us/itn) - copy.copy(old_list)
  • 0,075 сек (0,75us/itn) - list(old_list)
  • 0,053 сек (0,53us/itn) - new_list = []; new_list.extend(old_list)
  • 0.039 сек (0.39us/itn) - 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().

  • 2
    Поскольку вы проводите сравнительный анализ, может быть полезно включить контрольную точку. Являются ли эти цифры все еще точными в 2017 году при использовании Python 3.6 с полностью скомпилированным кодом? Я отмечаю ответ ниже ( stackoverflow.com/a/17810305/26219 ) уже ставит под сомнение этот ответ.
  • 2
    используйте модуль timeit . Кроме того, вы не можете сделать много выводов из произвольных микро тестов, как это.
Показать ещё 1 комментарий
132

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

newlist = old_list.copy()

95

Каковы варианты клонирования или копирования списка в 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'], [], []]

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

Не используйте 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
  • 0
    Вам не нужна глубокая копия, если список 2D. Если это список списков, и у этих списков нет списков внутри них, вы можете использовать цикл for. В настоящее время я использую list_copy=[] for item in list: list_copy.append(copy(item)) и это намного быстрее.
44

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

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)

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

32

new_list = list(old_list)

31

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

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

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

19

Все остальные участники предоставили ответы большие, которые работают, когда у вас есть один размерный (выровненный) список, однако из тех методов, о которых идет речь, только 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 >

  • 4
    Это не всегда будет работать, поскольку нет гарантии, что строки, возвращаемой repr() , достаточно для воссоздания объекта. Кроме того, eval() является инструментом последней инстанции; см. Эвал действительно опасен для SO таким ветераном Недом Батчелдером. Поэтому, когда вы пропагандируете использование eval() вам действительно следует упомянуть, что это может быть опасно.
  • 0
    Честная оценка. Хотя я думаю, что точка зрения Батчелдера в том, что наличие функции eval() в Python в целом является риском. Дело не в том, используете ли вы функцию в коде, а в том, что это дыра в безопасности Python сама по себе. Мой пример не использует его с функцией, которая получает входные данные от input() , sys.agrv или даже текстового файла. Это больше похоже на инициализацию пустого многомерного списка один раз, а затем просто способ скопировать его в цикл вместо повторной инициализации на каждой итерации цикла.
Показать ещё 1 комментарий
18

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

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

a = [1,2,3]

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

b = a

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

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

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

14

Начните с начала и исследователя немного глубоко:

Итак, у вас есть два списка:

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

Изображение 2458

Здесь в списке 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

Итак, поскольку мы можем видеть, что в нашем списке есть другой идентификатор, и это означает, что обе переменные указывают на разные объекты, так что на самом деле происходит следующее:

Изображение 2459

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

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[:], это происходит:

Изображение 2460

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

Итак, каково решение?

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

Изображение 2461

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

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

так что если мы делаем:

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

выход:

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

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

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

13

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))
  • 1
    Кажется, они оптимизировали инициализатор 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% быстрее
  • 0
    @Artyer Я снова протестировал с Python 3.6.3 (WSL 16.04) и получил в основном тот же порядок
8

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

Вы можете выполнить распаковку списка с помощью оператора "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)
  • 0
    Как ведет себя этот метод при изменении копий?
  • 1
    @ not2qubit вы имеете в виду добавление или редактирование элементов нового списка. В примере old_list и new_list - это два разных списка, редактирование одного не изменит другого (если вы непосредственно не изменили сами элементы (например, список списков), ни один из этих методов не является глубоким копированием).
Показать ещё 1 комментарий
5

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

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

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

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

newlist=list(oldlist)

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

4

Очень простой подход, не зависящий от версии 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
3
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)
0

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

-5

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

new_list = list(''.join(my_list))

Ещё вопросы

Сообщество Overcoder
Наверх
Меню