zoukankan      html  css  js  c++  java
  • [Python] 正确复制列表的方法

    new = old[:]

    Python老鸟都知道以上代码是什么意思。它复制列表old到new。它对于新手来说是种困惑而且应该避免使用这种方法。不幸的是[:]标记法被广泛使用,可能是Python程序员不知道更好的列表复制法吧。

    首先我们需要了解Python是如何管理对象和变量。Python没有C语言中的变量。在C语言中,变量不止是个名字,它是字节集合并真实存在于内存某个位置上。而在Python中,变量仅仅是指向对象的标签。

    看看以下语句:

    a = [1, 2, 3]

    它表示我们创建了一个指引指向列表[1, 2, 3],但是a不是列表。如果:

    b = a

    我们并没有复制a所指引的列表。我们只是创建了一个新的标签b,然后将其指向a所指向的列表。

    如果你修改a,那你就同时修改了b,因为它们指向同一个列表:

    >>> a = [1, 2, 3]
    >>> b = a
    >>> a.append(4)
    >>> print a
    [1, 2, 3, 4]
    >>> print b
    [1, 2, 3, 4]

    内建函数id()可以返回对象的唯一id。该id是对象的内存地址。

    >>> id(a)
    3086056
    >>> id(b)
    3086056
    >>> c = [] # Create a new list
    >>> id(c)
    2946712

    可以看出a和b都指向同一个内存地址。c指向一个新建的空列表,因此指向了不同的地址。

    现在我们要复制a指引的列表。我们必须创建新的列表,然后使用b指引它。

    这其实就是 new = old[:]。切片运算符[:]返回一个序列的切片。切片过程是切下列表的一部分,创建新的列表,将切下的部分复制到新列表。

    >>> a[1:3]
    [2, 3]
    >>> id(a)
    3086056
    >>> id(a[1:3])
    3063400

    省略第一个索引值,切片从列表开始,省略第二个索引值,切片直到列表末端。

    >>> a[:3]
    [1, 2, 3]
    >>> a[1:]
    [2, 3, 4]

    通过调用a[:],我们得到一个从列表首端开始到末端的切片,也就是a(指引的列表)的完整复制。但这不是复制列表的唯一方式。看看下面这个情况:

    >>> b = list(a)
    >>> id(a)
    3086056
    >>> id(b)
    3086256

    这个是不是看起来更好,少一些隐式,更加pythonic?a[:]看起来有点太像Perl。不同于切片标记法,不了解Python的人也会明白b是一个列表。

    list()是列表构造函数。它会在传入的数列基础上新建一个列表。数列不一定是列表,它可以是任何类型的数列。

    >>> my_tuple = (1, 2, 3)
    >>> my_list = list(my_tuple)
    >>> print my_list
    [1, 2, 3]
    >>> id(my_tuple)
    3084496
    >>> id(my_list)
    3086336

    而且它还接受生成器。切片笔记法不适用于生成器,因为生成器是不可更改。你不能generator[0],例如:

    >>> generator = (x * 3 for x in range(4))
    >>> list(generator)
    [0, 3, 6, 9]

    百分之九十的切片标记法都可以被list()代替。下次你看见[:]的时候试试使用list()替代,这样可以让你的代码更加可读。记住,魔鬼藏在细节里。

    附:五种复制方法的比较

    >>> import copy
    >>> a = [[10], 20]
    >>> b = a[:]
    >>> c = list(a)
    >>> d = a * 1
    >>> e = copy.copy(a)
    >>> f = copy.deepcopy(a)
    >>> a.append(21)
    >>> a[0].append(11)
    >>> print id(a), a
    30553152 [[10, 11], 20, 21]
    >>> print id(b), b
    44969816 [[10, 11], 20]
    >>> print id(c), c
    44855664 [[10, 11], 20]
    >>> print id(d), d
    44971832 [[10, 11], 20]
    >>> print id(e), e
    44833088 [[10, 11], 20]
    >>> print id(f), f
    44834648 [[10], 20]

    从以上可以看出,使用 a[:], list(a), a*1, copy.copy(a)四种方式复制列表结果都可以得到一个新的列表,但是如果列表中含有列表,所有b, c, d, e四个新列表的子列表都是指引到同一个对象上。只有使用copy.deepcopy(a)方法得到的新列表f才是包括子列表在内的完全复制。

    查看原文

  • 相关阅读:
    Codeforces Round #384 (Div. 2)
    Codeforces Round #383 (Div. 2)
    bzoj-4514(网络流)
    bzoj-4518 4518: [Sdoi2016]征途(斜率优化dp)
    bzoj-1096 1096: [ZJOI2007]仓库建设(斜率优化dp)
    hdu-5988 Coding Contest(费用流)
    hdu-5992 Finding Hotels(kd-tree)
    用链表实现杭电1276士兵队列训练问题
    循环链表
    图书管理系统
  • 原文地址:https://www.cnblogs.com/ifantastic/p/3811145.html
Copyright © 2011-2022 走看看