我如何克隆列表,以免在分配后意外变化?

发布于 2025-02-05 21:01:00 字数 282 浏览 5 评论 0 原文

在使用 new_list = my_list 时,对 new_list 更改 my_list 每次的任何修改。为什么这是这样,如何克隆或复制列表以防止它?例如:

>>> my_list = [1, 2, 3]
>>> new_list = my_list
>>> new_list.append(4)
>>> my_list
[1, 2, 3, 4]

While using new_list = my_list, any modifications to new_list changes my_list every time. Why is this, and how can I clone or copy the list to prevent it? For example:

>>> my_list = [1, 2, 3]
>>> new_list = my_list
>>> new_list.append(4)
>>> my_list
[1, 2, 3, 4]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

ぃ双果 2025-02-12 21:01:01

new_list = my_list 实际上并未创建第二个列表。分配仅复制对列表的引用,而不是实际列表,因此 new_list my_list 在分配后请参阅同一列表。

要实际复制列表,您有几个选项:

示例:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return f'Foo({self.val!r})'

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(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {f}')

结果:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]

new_list = my_list doesn't actually create a second list. The assignment just copies the reference to the list, not the actual list, so both new_list and my_list refer to the same list after the assignment.

To actually copy the list, you have several options:

  • You can use the built-in list.copy() method (available since Python 3.3):

    new_list = old_list.copy()
    
  • You can slice it:

    new_list = old_list[:]
    

    Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable).

  • You can use the built-in list() constructor:

    new_list = list(old_list)
    
  • You can use generic copy.copy():

    import copy
    new_list = copy.copy(old_list)
    

    This is a little slower than list() because it has to find out the datatype of old_list first.

  • If you need to copy the elements of the list as well, use generic copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)
    

    Obviously the slowest and most memory-needing method, but sometimes unavoidable. This operates recursively; it will handle any number of levels of nested lists (or other containers).

Example:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return f'Foo({self.val!r})'

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(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {f}')

Result:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
夜血缘 2025-02-12 21:01:01

Felix已经提供了一个很好的答案,但是我认为我会对各种方法进行速度比较:

  1. 10.59 sec(105.9 µs/itn) - copy.deepcopy(old_list)
  2. 10.16 sec(101.6 µs/itn) - 纯Python copy> copy> copy> copy()>方法与DeepCopy复制类
  3. 1.488秒(14.88 µs/itn) - 纯Python copy> copy()方法不复制类(仅dicts/lists/tarples)
  4. 0.325 sec(3.25 µs/itn) - >对于old_list中的项目:new_list.append(item)
  5. 0.217 sec(2.17 µs/itn) - [i in old_list] (a
  6. ​ /library/copy.html#复制。
  7. org )
  8. 0.053秒(0.53 µs/itn) - new_list = []; new_list.extend(old_list)
  9. 0.039 sec(0.39 µs/itn) - old_list [:] 列表切片),

因此最快的是列表切片。但是请注意, copy.copy() list [:] list(list),不同, copy> 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

Felix already provided an excellent answer, but I thought I'd do a speed comparison of the various methods:

  1. 10.59 sec (105.9 µs/itn) - copy.deepcopy(old_list)
  2. 10.16 sec (101.6 µs/itn) - pure Python Copy() method copying classes with deepcopy
  3. 1.488 sec (14.88 µs/itn) - pure Python Copy() method not copying classes (only dicts/lists/tuples)
  4. 0.325 sec (3.25 µs/itn) - for item in old_list: new_list.append(item)
  5. 0.217 sec (2.17 µs/itn) - [i for i in old_list] (a list comprehension)
  6. 0.186 sec (1.86 µs/itn) - copy.copy(old_list)
  7. 0.075 sec (0.75 µs/itn) - list(old_list)
  8. 0.053 sec (0.53 µs/itn) - new_list = []; new_list.extend(old_list)
  9. 0.039 sec (0.39 µs/itn) - old_list[:] (list slicing)

So the fastest is list slicing. But be aware that copy.copy(), list[:] and list(list), unlike copy.deepcopy() and the python version don't copy any lists, dictionaries and class instances in the list, so if the originals change, they will change in the copied list too and vice versa.

(Here's the script if anyone's interested or wants to raise any issues:)

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
兲鉂ぱ嘚淚 2025-02-12 21:01:01

被告知 href =“ http://bugs.python.org/issue10516” rel =“ noreferrer”>添加 list.copy.copy() 方法,该方法应该像切片一样快:

newlist = old_list.copy()

I've been told that Python 3.3+ adds the list.copy() method, which should be as fast as slicing:

newlist = old_list.copy()
眼眸 2025-02-12 21:01:01

在Python中克隆或复制列表的选项是什么?

在Python 3中,可以使用:

a_copy = a_list.copy()

在Python 2和3中进行浅副本,您可以获得带有原始完整切片的浅副本:

a_copy = a_list[:]

说明

有两种语义方法来复制列表。浅副本创建了相同对象的新列表,深层副本创建了一个包含新等价对象的新列表。

浅层列表复制

浅副本仅复制列表本身,该列表本身是对列表中对象的引用的容器。如果包含的对象是可变的,并且一个更改了,则更改将反映在两个列表中。

在Python 2和3中有不同的方法可以做到这一点。Python 2方法也将在Python3。Python2

中使用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.copy.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,然后每次my_list更改时都会修改new_list。为什么这是?

my_list 只是指向内存中实际列表的名称。当您说 new_list = my_list 您没有制作副本时,您只是在内存中添加了另一个名称。当我们制作清单副本时,我们可能会遇到类似的问题。

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

该列表只是内容的一系列指针,因此浅副本仅复制指针,因此您有两个不同的列表,但是它们具有相同的内容。为了制作内容的副本,您需要深层副本。

深副本

以使a 列表的深副本,在Python 2或3中,使用 deepcopy deepcopy < /code>在复制模块中

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

您可能会将其视为DeepCopy的一种方式,但不要这样做:

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

What are the options to clone or copy a list in Python?

In Python 3, a shallow copy can be made with:

a_copy = a_list.copy()

In Python 2 and 3, you can get a shallow copy with a full slice of the original:

a_copy = a_list[:]

Explanation

There are two semantic ways to copy a list. A shallow copy creates a new list of the same objects, a deep copy creates a new list containing new equivalent objects.

Shallow list copy

A shallow copy only copies the list itself, which is a container of references to the objects in the list. If the objects contained themselves are mutable and one is changed, the change will be reflected in both lists.

There are different ways to do this in Python 2 and 3. The Python 2 ways will also work in Python 3.

Python 2

In Python 2, the idiomatic way of making a shallow copy of a list is with a complete slice of the original:

a_copy = a_list[:]

You can also accomplish the same thing by passing the list through the list constructor,

a_copy = list(a_list)

but using the constructor is less efficient:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

In Python 3, lists get the list.copy method:

a_copy = a_list.copy()

In 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

Making another pointer does not make a copy

Using new_list = my_list then modifies new_list every time my_list changes. Why is this?

my_list is just a name that points to the actual list in memory. When you say new_list = my_list you're not making a copy, you're just adding another name that points at that original list in memory. We can have similar issues when we make copies of lists.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

The list is just an array of pointers to the contents, so a shallow copy just copies the pointers, and so you have two different lists, but they have the same contents. To make copies of the contents, you need a deep copy.

Deep copies

To make a deep copy of a list, in Python 2 or 3, use deepcopy in the copy module:

import copy
a_deep_copy = copy.deepcopy(a_list)

To demonstrate how this allows us to make new sub-lists:

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

And so we see that the deep copied list is an entirely different list from the original. You could roll your own function - but don't. You're likely to create bugs you otherwise wouldn't have by using the standard library's deepcopy function.

Don't use eval

You may see this used as a way to deepcopy, but don't do it:

problematic_deep_copy = eval(repr(a_list))
  1. It's dangerous, particularly if you're evaluating something from a source you don't trust.
  2. It's not reliable, if a subelement you're copying doesn't have a representation that can be eval'd to reproduce an equivalent element.
  3. It's also less performant.

In 64 bit 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

on 64 bit 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
风月客 2025-02-12 21:01:01

让我们从头开始探讨这个问题。

因此,让我们假设您有两个列表:

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

我们必须复制两个列表,现在从第一个列表开始:

因此,首先让我们尝试将变量复制设置为我们的原始列表, List_1

copy = list_1

现在,如果您正在考虑复制 list_1 的复制,那么您错了。 ID 函数可以向我们展示两个变量是否可以指向同一对象。让我们尝试一下:

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

因此,它修改了原始列表:

现在,让我们转到用于复制列表的Pythonic方法。

copy_1 = list_1[:]

此方法修复了我们遇到的第一个问题:

print(id(copy_1))
print(id(list_1))

4338792136
4338791432

因此,因为我们可以看到两个列表具有不同的ID,这意味着两个变量都指向不同的对象。因此,这里实际发生的是:

”在此处输入图像描述“

现在让我们尝试修改列表,让我们看看是否仍然遇到了先前的问题:

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 [:] 时,这发生了:

< img src =“ https://i.sstatic.net/3hpti.jpg” alt =“在此处输入图像说明”>

它创建了列表的副本,但只有外部列表副本,而不是嵌套的列表副本。两个变量的嵌套列表相同,因此,如果您尝试修改嵌套列表,则它将修改原始列表,因为两个列表的嵌套列表对象相同。

什么是解决方案?解决方案是 deepCopy 函数。

from copy import deepcopy
deep = deepcopy(list_2)

让我们检查一下:

print(id((list_2)), id(deep))

4322146056 4322148040

两个外部列表都有不同的ID。让我们在内部嵌套列表上尝试一下。

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

输出是:

4322145992
4322145800

您可以看到两个ID都不同,这意味着我们可以假设两个嵌套列表现在都指向不同的对象。

这意味着当您进行 deep = deepCopy(list_2)实际发生的事情时:

“在此处输入图像描述”

两个嵌套列表都指向不同的对象,并且它们现在具有嵌套列表的单独副本。

现在,让我们尝试修改嵌套列表,看看它是否解决了上一个问题:

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

它输出:

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

如您所见,它没有修改原始嵌套列表,它仅修改了复制的列表。

Let's start from the beginning and explore this question.

So let's suppose you have two lists:

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

And we have to copy both lists, now starting from the first list:

So first let's try by setting the variable copy to our original list, list_1:

copy = list_1

Now if you are thinking copy copied the list_1, then you are wrong. The id function can show us if two variables can point to the same object. Let's try this:

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

The output is:

4329485320
4329485320

Both variables are the exact same argument. Are you surprised?

So as we know, Python doesn't store anything in a variable, Variables are just referencing to the object and object store the value. Here object is a list but we created two references to that same object by two different variable names. This means that both variables are pointing to the same object, just with different names.

When you do copy = list_1, it is actually doing:

Enter image description here

Here in the image list_1 and copy are two variable names, but the object is same for both variable which is list.

So if you try to modify copied list then it will modify the original list too because the list is only one there, you will modify that list no matter you do from the copied list or from the original list:

copy[0] = "modify"

print(copy)
print(list_1)

Output:

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

So it modified the original list:

Now let's move onto a Pythonic method for copying lists.

copy_1 = list_1[:]

This method fixes the first issue we had:

print(id(copy_1))
print(id(list_1))

4338792136
4338791432

So as we can see our both list having different id and it means that both variables are pointing to different objects. So what actually going on here is:

Enter image description here

Now let's try to modify the list and let's see if we still face the previous problem:

copy_1[0] = "modify"

print(list_1)
print(copy_1)

The output is:

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

As you can see, it only modified the copied list. That means it worked.

Do you think we're done? No. Let's try to copy our nested list.

copy_2 = list_2[:]

list_2 should reference to another object which is copy of list_2. Let's check:

print(id((list_2)), id(copy_2))

We get the output:

4330403592 4330403528

Now we can assume both lists are pointing different object, so now let's try to modify it and let's see it is giving what we want:

copy_2[0][1] = "modify"

print(list_2, copy_2)

This gives us the output:

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

This may seem a little bit confusing, because the same method we previously used worked. Let's try to understand this.

When you do:

copy_2 = list_2[:]

You're only copying the outer list, not the inside list. We can use the id function once again to check this.

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

The output is:

4329485832
4329485832

When we do copy_2 = list_2[:], this happens:

Enter image description here

It creates the copy of list, but only outer list copy, not the nested list copy. The nested list is same for both variable, so if you try to modify the nested list then it will modify the original list too as the nested list object is same for both lists.

What is the solution? The solution is the deepcopy function.

from copy import deepcopy
deep = deepcopy(list_2)

Let's check this:

print(id((list_2)), id(deep))

4322146056 4322148040

Both outer lists have different IDs. Let's try this on the inner nested lists.

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

The output is:

4322145992
4322145800

As you can see both IDs are different, meaning we can assume that both nested lists are pointing different object now.

This means when you do deep = deepcopy(list_2) what actually happens:

Enter image description here

Both nested lists are pointing different object and they have separate copy of nested list now.

Now let's try to modify the nested list and see if it solved the previous issue or not:

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

It outputs:

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

As you can see, it didn't modify the original nested list, it only modified the copied list.

冷情妓 2025-02-12 21:01:01

已经有很多答案告诉您如何制作适当的副本,但是他们都没有说为什么您的原始“副本”失败了。

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)

请参阅文档有关复制角色的更多信息。

There are many answers already that tell you how to make a proper copy, but none of them say why your original 'copy' failed.

Python doesn't store values in variables; it binds names to objects. Your original assignment took the object referred to by my_list and bound it to new_list as well. No matter which name you use there is still only one list, so changes made when referring to it as my_list will persist when referring to it as new_list. Each of the other answers to this question give you different ways of creating a new object to bind to new_list.

Each element of a list acts like a name, in that each element binds non-exclusively to an object. A shallow copy creates a new list whose elements bind to the same objects as before.

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]

To take your list copy one step further, copy each object that your list refers to, and bind those element copies to a new list.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

This is not yet a deep copy, because each element of a list may refer to other objects, just like the list is bound to its elements. To recursively copy every element in the list, and then each other object referred to by each element, and so on: perform a deep copy.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

See the documentation for more information about corner cases in copying.

濫情▎り 2025-02-12 21:01:01

使用 thing [:]

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

Use thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
庆幸我还是我 2025-02-12 21:01:01

Python 3.6时间

是使用Python 3.6.8的定时结果。请记住,这些时候是相对的,而不是绝对的。

我坚持只做浅副本,还添加了一些新方法,这些方法是python&nbsp; 2中​​不可能的,例如 list.copy.copy.copy()(python&nbsp; 3 slice等效)和两种形式的列表解开包装*new_list,= list and code> new_list = [*list> list] ):

METHOD                TIME TAKEN
b = [*a]               2.75180600000021
b = a * 1              3.50215399999990
b = a[:]               3.78278899999986  # Python 2 winner (see above)
b = a.copy()           4.20556500000020  # Python 3 "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

我们可以请参阅Python 2获胜者仍然表现不错,但并没有大量考虑Python 3 list.copy.copy(),尤其是考虑到后者的出色可读性。

黑马是打开包装和重新包装方法( b = [*a] ),比原始切片快25%,速度是其他解压缩方法的两倍以上(> *b,= a )。

b = a * 1 也表现出色。

请注意,这些方法对除列表以外的任何输入而不是输出等效结果。它们都适用于切片对象,其中一些可用于任何觉得,但只有复制.copy()适用于更通用的Python对象。


这是感兴趣的各方的测试代码( template” ):

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))

Python 3.6 Timings

Here are the timing results using Python 3.6.8. Keep in mind these times are relative to one another, not absolute.

I stuck to only doing shallow copies, and also added some new methods that weren't possible in Python 2, such as list.copy() (the Python 3 slice equivalent) and two forms of list unpacking (*new_list, = list and new_list = [*list]):

METHOD                TIME TAKEN
b = [*a]               2.75180600000021
b = a * 1              3.50215399999990
b = a[:]               3.78278899999986  # Python 2 winner (see above)
b = a.copy()           4.20556500000020  # Python 3 "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

We can see the Python 2 winner still does well, but doesn't edge out Python 3 list.copy() by much, especially considering the superior readability of the latter.

The dark horse is the unpacking and repacking method (b = [*a]), which is ~25% faster than raw slicing, and more than twice as fast as the other unpacking method (*b, = a).

b = a * 1 also does surprisingly well.

Note that these methods do not output equivalent results for any input other than lists. They all work for sliceable objects, a few work for any iterable, but only copy.copy() works for more general Python objects.


Here is the testing code for interested parties (Template from here):

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))
活泼老夫 2025-02-12 21:01:01

python的习语是 newlist = oldlist [:]

Python's idiom for doing this is newList = oldList[:]

嘿看小鸭子会跑 2025-02-12 21:01:01

所有其他贡献者都给出了出色答案,当您具有单个维度(级别)列表时,这些答案可起作用,但是到目前为止提到的方法,只有 copy.deepcopy()当您使用多维的嵌套列表(列表列表)时,可以用克隆/复制列表,而没有指向嵌套的 list 对象。而 felix kling 在他的答案中指的是它,问题还有更多,可能还有一个解决工具的方法内置可能证明是 deepcopy 的更快替代方案。

new_list = old_list [:] copy.copy(old_list)'和py3k old_list.copy.copy()为单层列表工作,他们还原为指向 list 嵌套在 old_list new_list 中的对象,并更改为 list 对象在另一个中存在。

编辑:揭露新信息

正如两个 aaron Hall pm 2ring 使用 eval()不仅是一个坏主意,而且比 copy.deepcopy( )

这意味着对于多维列表,唯一的选项是 copy.deepcopy()。话虽如此,这确实不是一个选择,因为当您尝试在适度尺寸的多维阵列上使用时,性能就向南行驶。我尝试使用42x42数组来 timeit ,而不是闻所未闻的,甚至不是那么大的生物信息信息应用程序,我放弃了等待响应,刚刚开始将我的编辑键入此帖子。

>

似乎唯一真正的选择是初始化多个列表并独立处理它们。如果有人对如何处理多维列表复制,这将不胜感激。

正如其他人所说的那样, 使用复制模块和 copy.deepcopy 绩效问题强>用于多维列表

All of the other contributors gave great answers, which work when you have a single dimension (leveled) list, however of the methods mentioned so far, only copy.deepcopy() works to clone/copy a list and not have it point to the nested list objects when you are working with multidimensional, nested lists (list of lists). While Felix Kling refers to it in his answer, there is a little bit more to the issue and possibly a workaround using built-ins that might prove a faster alternative to deepcopy.

While new_list = old_list[:], copy.copy(old_list)' and for Py3k old_list.copy() work for single-leveled lists, they revert to pointing at the list objects nested within the old_list and the new_list, and changes to one of the list objects are perpetuated in the other.

Edit: New information brought to light

As was pointed out by both Aaron Hall and PM 2Ring using eval() is not only a bad idea, it is also much slower than copy.deepcopy().

This means that for multidimensional lists, the only option is copy.deepcopy(). With that being said, it really isn't an option as the performance goes way south when you try to use it on a moderately sized multidimensional array. I tried to timeit using a 42x42 array, not unheard of or even that large for bioinformatics applications, and I gave up on waiting for a response and just started typing my edit to this post.

It would seem that the only real option then is to initialize multiple lists and work on them independently. If anyone has any other suggestions, for how to handle multidimensional list copying, it would be appreciated.

As others have stated, there are significant performance issues using the copy module and copy.deepcopy for multidimensional lists.

倾城花音 2025-02-12 21:01:01

令我惊讶的是,尚未提及这一点,因此为了完整的目的,

您可以执行用“ Splat Operator”打开包装的列表:*,它也将复制列表的元素。

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)

It surprises me that this hasn't been mentioned yet, so for the sake of completeness...

You can perform list unpacking with the "splat operator": *, which will also copy elements of your list.

old_list = [1, 2, 3]

new_list = [*old_list]

new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]

The obvious downside to this method is that it is only available in Python 3.5+.

Timing wise though, this appears to perform better than other common methods.

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)
草莓酥 2025-02-12 21:01:01
new_list = my_list[:]

new_list = my_list

尝试理解这一点。假设 my_list 位于位置x,即, my_list 的位置。 '让 new_list 指向X。这被称为a 浅复制

现在,如果您将 new_list = my_list [:] 分配,您只是将每个对象复制到 my_list new_list 。这被称为A 深拷贝

您可以做到的其他方法是:

  •   new_list = list(old_list)
     
  •  导入复制
    new_list = copy.deepcopy(old_list)
     
new_list = my_list[:]

new_list = my_list

Try to understand this. Let's say that my_list is in the heap memory at location X, i.e., my_list is pointing to the X. Now by assigning new_list = my_list you're letting new_list point to the X. This is known as a shallow copy.

Now if you assign new_list = my_list[:], you're simply copying each object of my_list to new_list. This is known as a deep copy.

The other ways you can do this are:

  • new_list = list(old_list)
    
  • import copy
    new_list = copy.deepcopy(old_list)
    
终难愈 2025-02-12 21:01:01

一种非常简单的方法独立于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当我们不使用嵌套列表时。让我们检查一下当我们将解决方案#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

A very simple approach independent of python version was missing in already-given answers which you can use most of the time (at least I do):

new_list = my_list * 1       # Solution 1 when you are not using nested lists

However, if my_list contains other containers (for example, nested lists) you must use deepcopy as others suggested in the answers above from the copy library. For example:

import copy
new_list = copy.deepcopy(my_list)   # Solution 2 when you are using nested lists

.Bonus: If you don't want to copy elements use (AKA shallow copy):

new_list = my_list[:]

Let's understand difference between solution #1 and solution #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])

As you can see, solution #1 worked perfectly when we were not using the nested lists. Let's check what will happen when we apply solution #1 to nested lists.

>>> 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
苦妄 2025-02-12 21:01:01

我想发布一些与其他答案有所不同的内容。即使这很可能不是最容易理解或最快的选择,但它提供了一些内部视图,可以很好地了解深拷贝的工作原理,并且是深层复制的另一种选择。我的功能是否有错误并不重要,因为这样做的目的是显示出一种复制诸如“问题答案”之类的对象的方法,同时也将其作为解释DeepCopy在其核心方面的工作方式。

任何深层复制功能的核心都是制作浅副本的方法。如何?简单的。任何深层复制功能都只能复制不变对象的容器。当您深入嵌套列表时,您只会复制外部列表,而不是列表内部的可变对象。您仅重复容器。上课也相同的作品。当您深入一堂课时,您会深入副本所有可变属性。那怎么?您只需要复制列表,dict,元组,迭代,课程和课堂实例之类的容器?

很简单。一个可变的对象并不能真正重复。它永远无法更改,因此它只是一个值。这意味着您永远不必复制字符串,数字,布尔或任何一个。但是,您将如何复制容器?简单的。您只需初始化一个具有所有值的新容器。深拷贝依赖于递归。它复制了所有容器,甚至在其中包含容器,直到没有容器。容器是一个不变的对象。

一旦您知道这一点,就可以完全复制一个没有任何参考的对象。这是用于深编辑基本数据类型的功能(对自定义类不起作用,但您可以始终添加)

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自己的内置DeepCopy依靠该示例。唯一的区别是它支持其他类型,并且还通过将属性复制到新的重复类中来支持用户级,并以对对象的参考来阻止Infinite recursion,它已经使用备忘录列表或词典看到了它。这确实是为了制作深层副本。从本质上讲,制作深层副本只是制作浅副本。我希望这个答案为问题增加了一些东西。

示例

说您有此列表: [1,2,3] 。不变的数字不能重复,但另一层可以。您可以使用列表理解来复制它: [x for x in [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,bools,ploast,ints的任何列表甚至列表使用递归无限多层。在那里,您有它,深层复制。

tldr :DeepCopy使用递归来复制对象,并且仅返回与以前相同的不变对象,因为不可变的对象无法重复。但是,它深深地构成了可变物体的最内部层,直到到达对象的最外面可变层为止。

I wanted to post something a bit different than some of the other answers. Even though this is most likely not the most understandable, or fastest option, it provides a bit of an inside view of how deep copy works, as well as being another alternative option for deep copying. It doesn't really matter if my function has bugs, since the point of this is to show a way to copy objects like the question answers, but also to use this as a point to explain how deepcopy works at its core.

At the core of any deep copy function is way to make a shallow copy. How? Simple. Any deep copy function only duplicates the containers of immutable objects. When you deepcopy a nested list, you are only duplicating the outer lists, not the mutable objects inside of the lists. You are only duplicating the containers. The same works for classes, too. When you deepcopy a class, you deepcopy all of its mutable attributes. So, how? How come you only have to copy the containers, like lists, dicts, tuples, iters, classes, and class instances?

It's simple. A mutable object can't really be duplicated. It can never be changed, so it is only a single value. That means you never have to duplicate strings, numbers, bools, or any of those. But how would you duplicate the containers? Simple. You make just initialize a new container with all of the values. Deepcopy relies on recursion. It duplicates all the containers, even ones with containers inside of them, until no containers are left. A container is an immutable object.

Once you know that, completely duplicating an object without any references is pretty easy. Here's a function for deepcopying basic data-types (wouldn't work for custom classes but you could always add that)

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's own built-in deepcopy is based around that example. The only difference is it supports other types, and also supports user-classes by duplicating the attributes into a new duplicate class, and also blocks infinite-recursion with a reference to an object it's already seen using a memo list or dictionary. And that's really it for making deep copies. At its core, making a deep copy is just making shallow copies. I hope this answer adds something to the question.

EXAMPLES

Say you have this list: [1, 2, 3]. The immutable numbers cannot be duplicated, but the other layer can. You can duplicate it using a list comprehension: [x for x in [1, 2, 3]]

Now, imagine you have this list: [[1, 2], [3, 4], [5, 6]]. This time, you want to make a function, which uses recursion to deep copy all layers of the list. Instead of the previous list comprehension:

[x for x in _list]

It uses a new one for lists:

[deepcopy_list(x) for x in _list]

And deepcopy_list looks like this:

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

Then now you have a function which can deepcopy any list of strs, bools, floast, ints and even lists to infinitely many layers using recursion. And there you have it, deepcopying.

TLDR: Deepcopy uses recursion to duplicate objects, and merely returns the same immutable objects as before, as immutable objects cannot be duplicated. However, it deepcopies the most inner layers of mutable objects until it reaches the outermost mutable layer of an object.

情绪 2025-02-12 21:01:01

请注意,在某些情况下,如果您已经定义了自己的自定义类并且要保留属性,则应使用 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

Note that there are some cases where if you have defined your own custom class and you want to keep the attributes then you should use copy.copy() or copy.deepcopy() rather than the alternatives, for example in 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))

Outputs:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
国际总奸 2025-02-12 21:01:01

请记住,在Python中,当您使用时:

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

List2不是存储实际列表,而是对List1的引用。因此,当您对List1做任何事情时,List2也会更改。使用复制模块(不默认,在PIP上下载)来制作列表的原始副本( copy.copy() for简单列表, copy.deepcopy() for嵌套)。这制作了一个不会随第一个列表而变化的副本。

Remember that in Python when you do:

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

List2 isn't storing the actual list, but a reference to list1. So when you do anything to list1, list2 changes as well. use the copy module (not default, download on pip) to make an original copy of the list(copy.copy() for simple lists, copy.deepcopy() for nested ones). This makes a copy that doesn't change with the first list.

甜嗑 2025-02-12 21:01:01

通过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) 

A slight practical perspective to look into memory through id and 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) 
ぃ双果 2025-02-12 21:01:01

还有另一种复制列表到现在尚未列出的列表的方法:添加一个空列表: l2 = l + []

我用python 3.8进行了测试:

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

这不是最好的答案,但它起作用。

There is another way of copying a list that was not listed until now: adding an empty list: l2 = l + [].

I tested it with Python 3.8:

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

It is not the best answer, but it works.

分開簡單 2025-02-12 21:01:01

这是因为,行 new_list = my_list 为变量 my_list 分配新的引用
这类似于下面给出的 c 代码,

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

您应该使用复制模块来创建一个新列表

import copy
new_list = copy.deepcopy(my_list)

This is because, the line new_list = my_list assigns a new reference to the variable my_list which is new_list
This is similar to the C code given below,

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

You should use the copy module to create a new list by

import copy
new_list = copy.deepcopy(my_list)
浅语花开 2025-02-12 21:01:01

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

The deepcopy option is the only method that works for me:

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

leads to output of:

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]]]
-----------------------------
如何视而不见 2025-02-12 21:01:01

使用的方法取决于要复制列表的内容。如果该列表包含嵌套 dicts 而不是deepcopy是唯一有效的方法,否则答案中列出的大多数方法(slice,loop [for],复制,扩展,扩展,组合或解开)都可以使用并在类似的时间内执行(除了循环和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}
]}]

The method to use depends on the contents of the list being copied. If the list contains nested dicts than deepcopy is the only method that works, otherwise most of the methods listed in the answers (slice, loop [for], copy, extend, combine, or unpack) will work and execute in similar time (except for loop and deepcopy, which preformed the worst).

Script

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)

Results

[{"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}
]}]
梦醒灬来后我 2025-02-12 21:01:01

要了解不同的python克隆/复制选项,请查看此代码,该代码“复制”了嵌套列表的三种不同的方式,并使用 memory_graph 要绘制结果:

import memory_graph as mg # see link above for install instructions
import copy

a = [ [1, 2], ['x', 'y'] ] # a nested list (a list containing other lists)

# three different ways to make a "copy" of 'a':
c1 = a
c2 = copy.copy(a) # equivalent to:  a.copy() a[:] list(a)
c3 = copy.deepcopy(a)

mg.show(locals()) # show the graph
  • c1 sigsment ,没有复制,所有值共享
  • c2 is A 浅副本,仅复制第一个参考的值,所有基础值共享
  • c3 is a 深拷贝,所有值复制,什么都没有共享

全面披露:我是 memory_graph

To understand the different Python clone/copy options have a look at this code that "copies" a nested list three different ways and uses memory_graph to graph the result:

import memory_graph as mg # see link above for install instructions
import copy

a = [ [1, 2], ['x', 'y'] ] # a nested list (a list containing other lists)

# three different ways to make a "copy" of 'a':
c1 = a
c2 = copy.copy(a) # equivalent to:  a.copy() a[:] list(a)
c3 = copy.deepcopy(a)

mg.show(locals()) # show the graph
  • c1 is an assignment, nothing is copied, all the values are shared
  • c2 is a shallow copy, only the value referenced by the first reference is copied, all the underlying values are shared
  • c3 is a deep copy, all the values are copied, nothing is shared
    graph of three different "copies"

Full disclosure: I am the developer of memory_graph.

遮云壑 2025-02-12 21:01:01

框架挑战:您是否需要复制应用程序?

我经常看到试图以某种迭代方式修改列表副本的代码。要构建一个微不足道的示例,假设我们有不工作(因为 x 不应修改)代码,例如:

x = [8, 6, 7, 5, 3, 0, 9]
y = x
for index, element in enumerate(y):
    y[index] = element * 2
# Expected result:
# x = [8, 6, 7, 5, 3, 0, 9] <-- this is where the code is wrong.
# y = [16, 12, 14, 10, 6, 0, 18]

自然,人们会问如何制作 y 是<代码> x ,而不是同一列表的名称,因此循环的将做正确的事情。

但这是错误的方法。从功能上讲,我们真正想要做的是制作新列表,它是基于的原始>

我们不需要先制作副本就可以做到这一点,而且通常不应该这样做。

当我们需要将逻辑应用于每个元素时

,自然工具的自然工具就是列表的理解。这样,我们写下逻辑,告诉我们所需结果中的元素如何与原始元素相关。简单,优雅和富有表现力;而且,我们避免需要解决方法以修改 y 在 loop中复制复制(因为分配给迭代变量不会影响列表 - ,其原因是我们首先希望副本!)。

在上面的示例中,它看起来像:

x = [8, 6, 7, 5, 3, 0, 9]
y = [element * 2 for element in x]

列表综合功能非常强大;我们还可以使用它们来通过规则过滤元素,并使用子句子句,并且我们可以为 and and 链条(如果> renauses)(它的工作方式)相同的条款以相同的顺序 的相应代码;如果计划在修改副本“>避免问题)的情况下进行修改通过过滤列表的理解。

当我们需要按位置拒绝或插入特定元素时,

假设我们有类似的东西,而

x = [8, 6, 7, 5, 3, 0, 9]
y = x
del y[2:-2] # oops, x was changed inappropriately

不是首先制作 y 首先删除我们不想要的零件,我们可以构建一个列表通过将我们 do 的零件放在一起。因此:

x = [8, 6, 7, 5, 3, 0, 9]
y = x[:2] + x[-2:]

通过切片处理插入,替换等。恰恰理解您希望结果包含哪些子序列。这样的一种特殊情况是进行反向副本 - 假设我们完全需要一个新列表(而不是仅仅是<倒向),我们可以直接通过切割而不是克隆,然后使用 reververs 来直接创建它。


这些方法(例如列表理解)也具有将所需结果创建为表达式的优势,而不是通过程序将现有对象定为就地(和

Frame challenge: do you actually need to copy, for your application?

I often see code that tries to modify a copy of the list in some iterative fashion. To construct a trivial example, suppose we had non-working (because x should not be modified) code like:

x = [8, 6, 7, 5, 3, 0, 9]
y = x
for index, element in enumerate(y):
    y[index] = element * 2
# Expected result:
# x = [8, 6, 7, 5, 3, 0, 9] <-- this is where the code is wrong.
# y = [16, 12, 14, 10, 6, 0, 18]

Naturally people will ask how to make y be a copy of x, rather than a name for the same list, so that the for loop will do the right thing.

But this is the wrong approach. Functionally, what we really want to do is make a new list that is based on the original.

We don't need to make a copy first to do that, and we typically shouldn't.

When we need to apply logic to each element

The natural tool for this is a list comprehension. This way, we write the logic that tells us how the elements in the desired result, relate to the original elements. It's simple, elegant and expressive; and we avoid the need for workarounds to modify the y copy in a for loop (since assigning to the iteration variable doesn't affect the list - for the same reason that we wanted the copy in the first place!).

For the above example, it looks like:

x = [8, 6, 7, 5, 3, 0, 9]
y = [element * 2 for element in x]

List comprehensions are quite powerful; we can also use them to filter out elements by a rule with an if clause, and we can chain for and if clauses (it works like the corresponding imperative code, with the same clauses in the same order; only the value that will ultimately end up in the result list, is moved to the front instead of being in the "innermost" part). If the plan was to iterate over the original while modifying the copy to avoid problems, there is generally a much more pleasant way to do that with a filtering list comprehension.

When we need to reject or insert specific elements by position

Suppose instead that we had something like

x = [8, 6, 7, 5, 3, 0, 9]
y = x
del y[2:-2] # oops, x was changed inappropriately

Rather than making y a separate copy first in order to delete the part we don't want, we can build a list by putting together the parts that we do want. Thus:

x = [8, 6, 7, 5, 3, 0, 9]
y = x[:2] + x[-2:]

Handling insertion, replacement etc. by slicing is left as an exercise. Just reason out which subsequences you want the result to contain. A special case of this is making a reversed copy - assuming we need a new list at all (rather than just to iterate in reverse), we can directly create it by slicing, rather than cloning and then using .reverse.


These approaches - like the list comprehension - also have the advantage that they create the desired result as an expression, rather than by procedurally modifying an existing object in-place (and returning None). This is more convenient for writing code in a "fluent" style.

初吻给了烟 2025-02-12 21:01:01

每个副本模式的简短说明:

a 浅副本构造一个新的复合对象,然后(在可能的程度上)将引用插入到原始中的对象中 - 创建浅副本

new_list = my_list

深层复制构建一个新的复合对象,然后递归地将副本插入原始的对象中 - 创建一个深拷贝:

new_list = list(my_list)

list()在深处工作正常简单列表的副本,例如:

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

但是,对于

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

...

import copy
new_complex_list = copy.deepcopy(my_complex_list)

Short and simple explanations of each copy mode:

A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original - creating a shallow copy:

new_list = my_list

A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original - creating a deep copy:

new_list = list(my_list)

list() works fine for deep copy of simple lists, like:

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

But, for complex lists like...

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

...use deepcopy():

import copy
new_complex_list = copy.deepcopy(my_complex_list)
薄情伤 2025-02-12 21:01:01
new_list = my_list

因为: new_list 将仅是对 my_list 的参考,而在 new_list 中进行的更改也将在 my_list 反之亦然,

有两种复制列表

new_list = my_list.copy()

new_list = list(my_list)
new_list = my_list

because: new_list will only be a reference to my_list, and changes made in new_list will automatically also be made in my_list and vice versa

There are two easy ways to copy a list

new_list = my_list.copy()

or

new_list = list(my_list)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文