【问题标题】:Does [:] slice only make shallow copy of a list?[:] 切片是否只制作列表的浅表副本?
【发布时间】:2015-02-22 03:33:36
【问题描述】:

我遇到了来自这个[:] 副本的特殊错误。

The docs say [:] 只做一个浅拷贝,但似乎:

a = [1,2,3]
id(a)
3071203276L
b=a[:]
id(b)
3071234156L

id(a)不等于id(b);那怎么只是一个浅拷贝?

特例:

import numpy as np
import random
a = np.array([1,2,3])
b=a[:]
random.shuffle(a)

b相应变化。

【问题讨论】:

  • a 和 b 的 id 与人们对副本的期望不同。有什么问题?
  • 它是浅拷贝,因为深拷贝很昂贵——而且通常不受欢迎。
  • Slice 是浅拷贝。但无论是浅层还是深层,顶层对象始终是一个副本。
  • 见下文:id() 无法区分深拷贝和浅拷贝。仅复制与非复制 (b=a; print id(a), id(b))。
  • Numpy 提出了一个不同的问题。 Numpy 切片意味着您只是在复制 index,而不是后备存储。您甚至可以通过这种方式更改数组的形状。它们被设计成看起来像 python 列表,但实际上并非如此。

标签: python numpy


【解决方案1】:

区别在于列表a和列表b的元素是相同的。更改任一列表中的可变对象也会影响另一个列表中的元素。

相比之下,deepcopy 尝试创建一组完全不同的对象。

【讨论】:

    【解决方案2】:

    可能只有deepcopy才能进行深层复制。

    因为深拷贝很昂贵,因为您需要跟踪您复制的每个对象。

    考虑这样的邪恶结构:

    a = []
    a.append(a)
    

    您当然不想对此进行天真的深拷贝。

    所以是的,这是一个浅拷贝。但是在您的示例中,它正在存储原语,这些原语将被复制为值,而不是引用。所以修改一个列表不会修改另一个。

    id()不能用于区分浅拷贝和深拷贝

    它将副本与非副本(具有相同的 id)区分开来。

    a = [1, 2, 3]
    b = a
    print id(b), id(a), "no surprise, same id, no copy."
    

    Numpy 数组不同

    在这里,您只“浅拷贝”索引,而不是后备存储中的数据。如果您想确保有一份副本,请使用.copy()

    【讨论】:

      【解决方案3】:

      这是一个浅拷贝,但在这种情况下更改b 不会影响a,因为元素只是数字。如果它们是引用,那么 a 将被更新:

      a = [1, 2, 3]
      b = a[:]
      
      b[1] = 5
      print "a: ", a
      print "b: ", b
      # a: [1, 2, 3]
      # b: [1, 5, 3]
      

      a = [[1], [2], [3]]
      b = a[:]
      
      b[1][0] = 5
      print "a: ", a
      print "b: ", b
      # a:  [[1], [5], [3]]
      # b:  [[1], [5], [3]]
      

      【讨论】:

      • 它是否解释了更新线程中 numpy 数组的情况?谢谢
      • 我对 numpy 的了解不够,无法正确回答该案例。
      【解决方案4】:

      Numpy 定义切片返回的内容与标准 python 库不同。这是因为 numpy 可以处理大量数据。并不总是需要复制这些巨大的数组,尤其是当用户只想要数组的临时视图时。例如,arr[:100].sum() 对前 100 个元素求和,而不必创建前 100 个元素的临时浅表副本。请注意,仅为 basic 切片创建临时视图。

      有关详细信息,请参阅documentation

      【讨论】:

        【解决方案5】:

        粗略的回答:

        numpy 中的数组是 后备存储上的视图/索引

        您可以复制视图,而无需复制后备存储...

        a=numpy.array([1,2,3,4])
        b=a[:] # copy of the array ("view" or "index"), not the storage
        b.shape=(2,2)
        print a
        # [1 2 3 4]
        print b
        # [[1 2]
        #  [3 4]]
        b *= 2
        print a
        # [2 4 6 8]
        print b
        # [[2 4]
        #  [6 8]]
        

        看看改变 b 是如何影响 a 的?然而,它们仍然具有不同的形状。将它们视为数据视图;而b=a[:] 行仅复制了此视图。我什至可以修改b 的形状。因为它只是数据的索引,它表示列和行在内存中的位置。

        如果您想要 numpy 中的后备存储副本,请使用 a.copy()

        【讨论】:

          猜你喜欢
          • 2013-04-22
          • 1970-01-01
          • 1970-01-01
          • 2022-11-10
          • 1970-01-01
          • 2021-11-18
          • 2019-11-16
          • 2011-07-05
          相关资源
          最近更新 更多