考虑下列代码片段:
1 | ls = [[]] * 5 |
第 2,5,8,11 行将输出什么结果?请解释。
输出的结果如下:
1 | [[], [], [], [], []] |
解释如下:
默认情况下,我们认为序列是支持 +
和 *
操作的。通常 +
号两侧的序列由相同类型的数据所构成,在拼接的过程中,两个被操作的序列都不会被修改,Python 会新建一个包含同样类型数据的序列来作为拼接的结果。如果想要把一个序列复制几份然后再拼接起来,更快捷的做法是把这个序列乘以一个整数。同样,这个操作会产生一个新序列:
1 | 1, 2, 3] ls = [ |
Python 中 +
和 *
都遵循这个规律,不修改原有的操作对象,而是构建一个全新的序列。
1 | ls = [[]] * 5 |
其中 [[]]
是一列表中包含了一个空列表,即列表的第一个位置指向一个空的列表:
要格外注意的是,在 ls = [[]] * 5
这个语句中,得到的列表 ls
里面包含的 5 个元素(空列表)其实是 5 个对象引用,而且这 5 个引用指向的都是同一个对象。如图所示:
1 | ls[0].append(10) |
这个语句是在 ls
的第 1 个元素所指向的列表对象中追加一个元素 10
。但由于 ls
的 5 个元素是引用的同一个列表,所以这个结果将是 [[10], [10], [10], [10], [10]]
。即
1 | ls[1].append(20) |
同理,这个语句是在 ls
的第 2 个元素所指向的列表对象中追加一个元素 10
。但由于 ls
的 5 个元素同样是引用的同一个列表,所以输出结果现在是 [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
。即:
1 | ls.append(30) |
作为对比,这个语句是在外列表上追加了新的元素 30
,因此产生的结果是: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]
。也就是: