【问题标题】:python list by value not by reference [duplicate]python按值列出而不是通过引用[重复]
【发布时间】:2012-02-03 08:56:24
【问题描述】:

举个例子

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

    【讨论】:

      【解决方案3】:

      另外,你可以这样做:

      b = list(a)
      

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

      【讨论】:

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

      【讨论】:

        【解决方案5】:

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

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

        b = a[:]
        

        【讨论】:

        • 只是技术性问题,但 python 变量并不是真正的指针。更准确地说,当您执行 b = a 时,您创建了对 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.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[:] 的两倍?
          【解决方案9】:

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

          b = a[:]
          

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

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

          【讨论】:

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

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

          b = a[:]
          

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

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

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

          from copy import deepcopy
          
          b = deepcopy(a)
          

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

          【讨论】:

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

          我会推荐以下解决方案:

          b = []
          b[:] = a
          

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

          【讨论】:

          • 这不比b = a[:]
          猜你喜欢
          • 2017-09-04
          • 2017-03-15
          • 2013-09-13
          • 1970-01-01
          • 2023-01-30
          • 1970-01-01
          • 2021-01-18
          相关资源
          最近更新 更多