【问题标题】:python list by value not by reference [duplicate]python按值列出而不是按引用[重复]
【发布时间】:2017-09-04 03:58:49
【问题描述】:

举个例子

a=['help', 'copyright', 'credits', 'license']
b=a
b.append('XYZ')
b
['help', 'copyright', 'credits', 'license', 'XYZ']
a
['help', 'copyright', 'credits', 'license', 'XYZ']

我想在列表“b”中附加值,但列表“a”的值也发生了变化。
我想我不知道为什么会这样(python 通过引用传递列表)。
我的问题是“如何按值传递它,以便附加 'b' 不会改变 'a' 中的值?”

【问题讨论】:

    标签: python list reference


    【解决方案1】:

    您不能在 Python 中按值传递任何内容。如果您想复制a,您可以明确地这样做,如official Python FAQ 中所述:

    b = a[:]
    

    【讨论】:

    • 对我不起作用。我对b 所做的任何更改也会在a 中看到。
    • @Mannix 你能在新问题中发布full code 来显示问题(即断言应该失败)吗?最有可能的是,您不是在修改列表本身,而是在修改它的元素。如果您想要一个其元素也是副本的新列表,请创建一个 deep copy。
    • 但是,如果 a 是二维列表,这将不起作用
    • 对于二维数组,可以使用 map 函数: old_array = [[2, 3], [4, 5]] # python2.* new_array = map(list, old_array) # python3.* new_array =列表(地图(列表,旧数组))
    • @Pythoner 您描述的代码适用于二维列表,而不是数组。 copy.deepcopy(something) 两者都适用。但话又说回来,如果您的列表是 2D 的 - 或任何数据结构,但一个简单的列表 - 那么您的问题与此处的问题不同。
    【解决方案2】:

    如果要复制一维列表,请使用

    b = a[:]
    

    但是,如果a 是一个二维列表,那么这对您不起作用。也就是说,a 中的任何更改也将反映在b 中。在这种情况下,使用

    b = [[a[x][y] for y in range(len(a[0]))] for x in range(len(a))]
    

    【讨论】:

    • 并非真的会反映 任何 更改 - 只有当原始列表中的列表发生更改时,它才会反映在副本上。其他数据更改不会反映在其他副本上,因此字符串或整数可以安全更改
    【解决方案3】:

    我会推荐以下解决方案:

    b = []
    b[:] = a
    

    这会将所有元素从 a 复制到 b。该副本将是价值副本,而不是参考副本。

    【讨论】:

    • 这还不如b = a[:]
    【解决方案4】:

    正如 phihag 在他的回答中提到的,

    b = a[:]
    

    将适用于您的情况,因为切片列表会创建列表的新内存 id(这意味着您不再引用内存中的相同对象,并且您对其中一个对象所做的更改不会反映在另一个对象中。)

    但是,有一个小问题。如果你的列表是多维的,就像列表中的列表一样,简单的切片并不能解决这个问题。在更高维度(即原始列表中的列表)中所做的更改将在两者之间共享。

    别着急,有办法的。模块副本有一个很好的复制技术可以解决这个问题。

    from copy import deepcopy
    
    b = deepcopy(a)
    

    无论列表包含多少层级,都将复制具有新内存 id 的列表!

    【讨论】:

    • 很好的答案,乔丹!谢谢!!!你知道这是什么原因吗?
    • 很好的答案,尤其是当它提到了初始解决方案将失败的情况(嵌套对象、其他对象列表)和解决方案( deepcopy() )。
    【解决方案5】:

    就性能而言,我最喜欢的答案是:

    b.extend(a)
    

    检查相关替代方案在性能方面的比较:

    In [1]: import timeit
    
    In [2]: timeit.timeit('b.extend(a)', setup='b=[];a=range(0,10)', number=100000000)
    Out[2]: 9.623248100280762
    
    In [3]: timeit.timeit('b = a[:]', setup='b=[];a=range(0,10)', number=100000000)
    Out[3]: 10.84756088256836
    
    In [4]: timeit.timeit('b = list(a)', setup='b=[];a=range(0,10)', number=100000000)
    Out[4]: 21.46313500404358
    
    In [5]: timeit.timeit('b = [elem for elem in a]', setup='b=[];a=range(0,10)', number=100000000)
    Out[5]: 66.99795293807983
    
    In [6]: timeit.timeit('for elem in a: b.append(elem)', setup='b=[];a=range(0,10)', number=100000000)
    Out[6]: 67.9775960445404
    
    In [7]: timeit.timeit('b = deepcopy(a)', setup='from copy import deepcopy; b=[];a=range(0,10)', number=100000000)
    Out[7]: 1216.1108016967773
    

    【讨论】:

    • 感谢您将性能引入讨论,这有助于我决定使用哪种方法。
    • 我刚刚找到您的答案,感谢您的高质量回复!在讨论 Python 时,通常不会考虑性能,而对于庞大的数据集,性能会有所不同。
    • 我喜欢这个答案,但是,它与列表的值无关。正如 Jordan Pagni 所提到的,如果您的列表是多维的,例如列表中的列表(以及更多),那么唯一可行的解​​决方案是耗时最长的解决方案:b = deepcopy(a)
    • extend() 调用的测试用例无法与其他人相比。要使用extend(),您必须先创建一个数组,而其他构造会为您创建一个数组。因此,您可以通过跳过列表对象的初始化来有效地为extend() 提供优势。要更正测试,请将b = [] 从设置移动到测试下的语句,例如b = []; b.extend(a)。这将改变结果,支持使用切片创建副本的第二种情况。
    • 为什么b=list(a) 花费的时间是b=a[:] 的两倍?
    【解决方案6】:

    要复制列表,您可以使用list(a) 或a[:]。在这两种情况下,都会创建一个新对象。
    然而,这两种方法对可变对象的集合有限制,因为内部对象保持其引用不变:

    >>> a = [[1,2],[3],[4]]
    
    >>> b = a[:]
    >>> c = list(a)
    
    >>> c[0].append(9)
    
    >>> a
    [[1, 2, 9], [3], [4]]
    >>> c
    [[1, 2, 9], [3], [4]]
    >>> b
    [[1, 2, 9], [3], [4]]
    >>> 
    

    如果你想要你的对象的完整副本,你需要copy.deepcopy

    >>> from copy import deepcopy
    >>> a = [[1,2],[3],[4]]
    
    >>> b = a[:]
    >>> c = deepcopy(a)
    
    >>> c[0].append(9)
    
    >>> a
    [[1, 2], [3], [4]]
    >>> b
    [[1, 2], [3], [4]]
    >>> c
    [[1, 2, 9], [3], [4]]
    >>> 
    

    【讨论】:

    • 普通拷贝和深拷贝有什么区别?为什么上面发生的事情会发生?我想我有个大概的了解,好像和op在第二层遇到的一样的问题。它在内部是如何运作的?
    【解决方案7】:

    发现我们可以使用extend()来实现copy()的功能

    a=['help', 'copyright', 'credits', 'license']
    b = []
    b.extend(a)
    b.append("XYZ") 
    

    【讨论】:

      【解决方案8】:

      当您执行b = a 时,您只需创建另一个指向a 的同一内存的指针, 这就是为什么当您附加到 b 时,a 也会发生变化。

      您需要创建 a 的副本,操作如下:

      b = a[:]
      

      【讨论】:

      • 只是技术性问题,但 python 变量并不是真正的指针。更准确地说,当您执行 b = a 时,您创建了对 a 引用的列表对象的另一个引用。
      【解决方案9】:

      【讨论】:

        【解决方案10】:

        另外,你可以这样做:

        b = list(a)
        

        这适用于任何序列,即使是那些不支持索引器和切片的序列...

        【讨论】:

        • 请注意,这种方法也不能完全适用于多维列表 - 因此,如果您在原始列表中有一个列表,如果在一个副本中更改,它将在任何地方发生变化
        【解决方案11】:

        要创建列表的副本,请执行以下操作:

        b = a[:]
        

        【讨论】:

          猜你喜欢
          • 2012-02-03
          • 2017-03-15
          • 1970-01-01
          • 2011-02-07
          • 2015-10-14
          • 2020-04-15
          • 1970-01-01
          相关资源
          最近更新 更多