python 正确复制列表的方法

正确复制列表方法

new = old[:]

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

容易遇到的坑

新手可能是从java等过来学习python的,会自然而然使用以下这种复制方法,但实际上这是不正确的。

new = old

首先我们需要了解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[:],我们得到一个从列表首端开始到末端的切片,也就是a(指引的列表)的完整复制。

b = a[:]

方法二:list() (适用各种传入的数列,包括生成器)

b = list(a)

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]

方法三:*1法 (不常用)

b = a * 1

方法四:copy.copy()(不常用)

import copy
b = copy.copy(a)

方法五:copy.deepcopy() (使用有子列表的列表,完全复制)

import copy
b = copy.deepcopy(a)

复制列表方法的比较

>>> 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才是包括子列表在内的完全复制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值