Python中的必考问题3

考虑下列代码片段:

1
2
3
4
5
6
7
8
9
10
11
ls = [[]] * 5
print(ls) # output?

ls[0].append(10)
print(ls) # output?

ls[1].append(20)
print(ls) # output?

ls.append(30)
print(ls) # output?

第 2,5,8,11 行将输出什么结果?请解释。

输出的结果如下:

1
2
3
4
[[], [], [], [], []]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]

解释如下:

默认情况下,我们认为序列是支持 +* 操作的。通常 + 号两侧的序列由相同类型的数据所构成,在拼接的过程中,两个被操作的序列都不会被修改,Python 会新建一个包含同样类型数据的序列来作为拼接的结果。如果想要把一个序列复制几份然后再拼接起来,更快捷的做法是把这个序列乘以一个整数。同样,这个操作会产生一个新序列:

1
2
3
4
5
>>> ls = [1, 2, 3]
>>> ls * 5
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> 'abc' * 5
'abcabcabcabcabc'

Python 中 +* 都遵循这个规律,不修改原有的操作对象,而是构建一个全新的序列。

1
2
ls = [[]] * 5
print(ls) # output?

其中 [[]] 是一列表中包含了一个空列表,即列表的第一个位置指向一个空的列表:

要格外注意的是,在 ls = [[]] * 5 这个语句中,得到的列表 ls 里面包含的 5 个元素(空列表)其实是 5 个对象引用,而且这 5 个引用指向的都是同一个对象。如图所示:

1
2
ls[0].append(10)
print(ls) # output?

这个语句是在 ls 的第 1 个元素所指向的列表对象中追加一个元素 10。但由于 ls 的 5 个元素是引用的同一个列表,所以这个结果将是 [[10], [10], [10], [10], [10]] 。即

1
2
ls[1].append(20)
print(ls) # output?

同理,这个语句是在 ls 的第 2 个元素所指向的列表对象中追加一个元素 10。但由于 ls 的 5 个元素同样是引用的同一个列表,所以输出结果现在是 [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]。即:

1
2
ls.append(30)
print(ls) # output?

作为对比,这个语句是在外列表上追加了新的元素 30,因此产生的结果是: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]。也就是:

有钱任性,请我吃包辣条
0%