zoukankan      html  css  js  c++  java
  • Shallow Copy & Deep Copy in Python list

      今天在写一个小程序的时候用到了2维数组, 顺手就写成了[[0.0]*length]*length, 结果为了这个小错,调试了半个多小时,

    其实之前对与浅复制和深复制已经做过学习和总结, 但真正编程用到这些知识时还是掉入了陷阱中. 所以在此做进一步的总结:

      本文通过几个实例来说明Python中list的深复制和浅复制:

    >>> a = [[]] * 10
    >>> a
    [[], [], [], [], [], [], [], [], [], []]
    >>> a[0][0] = 10    #NO WAY
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IndexError: list assignment index out of range
    >>> a[0].append(1)
    >>> a
    [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]

      a[0].append(1)后, 如果a的输出结果让你感到有些困惑,你可以参考这里(原因是Python中的*运算采用的是浅复制).

      同样的道理,下面的代码我们应该都能够理解:

    >>> a[3].append(9)
    >>> a
    [[1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9]]
    >>> a[2][1] = 3
    >>> a
    [[1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3]]

      让我们一起来分析一下:

      对于a(理解为一个2维数组)中的每一个元素都是一个list(理解为一个1维数组), 但我们需要注意的是a的每一个元素

    a[0],a[1],a[2]...指向的是同一段内存区域(浅复制),所以更改(修改值或添加值)任何一个元素(a[0]或a[1]...a[9])都会直接影

    响到其它的元素.

      如何验证a中的每一个元素a[0], a[1],...,a[9]指向同一段内存区域? 可以通过id方法来验证:

    >>> id.__doc__
    "id(object) -> integer

    Return the identity of an object. This is guaranteed to be unique among
    simultaneously existing objects. (Hint: it's the object's memory address.)
    "
    >>> a = [[]]*10
    >>> a
    [[], [], [], [], [], [], [], [], [], []]
    >>> id(a[0])
    3071938316L
    >>> id(a[1])
    3071938316L
    >>> a[0].append(1)
    >>> a
    [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
    >>> id(a[0])
    3071938316L
    >>> id(a[1])
    3071938316L
    >>> 

    注意:虽然a的每一个元素a[0],a[1],a[2]...指向的是同一段内存区域,但a中的各个元素是独立的元素(他们相同但不同一),

    也就是说删除掉任何一个数据对其他的数据没有任何影响:

    >>> a = [[]] * 10
    >>> a
    [[], [], [], [], [], [], [], [], [], []]
    >>> a[0].append(1)
    >>> a
    [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
    >>> len(a)
    10
    >>> del(a[2])
    >>> a
    [[1], [1], [1], [1], [1], [1], [1], [1], [1]]
    >>> len(a)
    9

      那么应该怎么实现深复制呢?其实在前面提到的文章中已经介绍了这一方法:  

    >>> c = [[] for i in range(10)]
    >>> c
    [[], [], [], [], [], [], [], [], [], []]
    >>> c[0].append(3)
    >>> c
    [[3], [], [], [], [], [], [], [], [], []]
    >>> 

      至此, 我觉得还有一点需要说明:

      一定要理解*操作的对象是谁, 例如: [2]*10得到[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], *10操作的对象是[]中的2, 也就是说*10操

    作使list中的元素2复制10次. 同理[[]]*10得到[[], [], [], [], [], [], [], [], [], []],*10操作的对象是[]中的[], 也就是说*10

    操作使list中的元素[]浅复制10次, 这10个空list指向内存中相同的区域(参见上面用id验证部分).

      下面用2段代码作为对比列出来, 便于查看:

    >>> a = [2] * 10
    >>> a
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
    >>> id(a[0])
    164067492
    >>> id(a[1])
    164067492
    >>> a[0] = 1  #NOTE
    >>> id(a[0])  #NOTE
    164067504
    >>> id(a[1])
    164067492
    >>> 
    >>> b = [[]] * 10
    >>> b
    [[], [], [], [], [], [], [], [], [], []]
    >>> id(b[0])
    3072965964L
    >>> id(b[1])
    3072965964L
    >>> b[0].append(10)
    >>> id(b[0])
    3072965964L
    >>> id(b[1])
    3072965964L
    >>> b
    [[10], [10], [10], [10], [10], [10], [10], [10], [10], [10]]
    >>> b[0][0] = 1
    >>> b
    [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
    >>> id(b[0])
    3072965964L
    >>> id(b[1])
    3072965964L
    >>> b[0] = [10]
    >>> b
    [[10], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
    >>> id(b[1])
    3072965964L
    >>> id(b[0])
    3072965996L
    >>> 
  • 相关阅读:
    Mybatis plus 配置
    logback配置
    iview-ui-project-4.0 安装与配置
    Linux系统下Redis安装与配置
    Java中枚举的用法
    Mysql 查询所有课程的成绩第2名到第3名的学生信息及该课程成绩
    java 基础知识一 初识java
    docker 查看 挂载目录
    sqlserver统计所有表及表中记录数
    centos7配置禁用ipv6
  • 原文地址:https://www.cnblogs.com/lxw0109/p/shallow-copy-and-deep-copy.html
Copyright © 2011-2022 走看看