创建列表的正确姿势

---

此文并不介绍python中创建列表的各种方式,而源于写基数排序时的一个小Bug


在写基数排序的时候,创建了一个表中表:

1
a = [[]] * 10

这事用来存放对应位数上的元素的。在append元素的时候发现所有子列表是一起变化的。很自然的想到子列表是共用同一个地址的。查看一下内存地址果然如此。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = [[]] * 10
for i in a:
print(id(i))

[out]:
2502709438920
2502709438920
2502709438920
2502709438920
2502709438920
2502709438920
2502709438920
2502709438920
2502709438920
2502709438920

不用理会在内存中的具体地址是多少,只需要知道每一个列表的地址是一样的就可以了。列表的对应关系如下图所示。

image-20210422145549029
image-20210422145549029

我们知道,在python中给列表赋值有直接赋值(添加一个地址指针)和拷贝赋值(copy)等方式。

1
2
3
a = [1,2,3]
b = a
c = a.copy()

在上面的例子中,b和a变量指向的内存地址是一样的,改变a或b的值另外一个也会相应的变化,而c则是独立出来的新变量,c的值与ab的值之间不会相互影响,他们的关系如下:

image-20210422150330792
image-20210422150330792

采用a = [[]] * 10这种方式创建10个子空列表相当于将一个空列表复制十遍但是指向地址还是同一个,所以改变其中一个子列表的值其他子列表的值相应的发生改变。


那么正确的创建方式是什么呢。亲测使用[[] for _ in range(10)]可以创建相互独立的子列表。此方式在创建的时候每一次循环都单独生成一个新的地址空间上的列表来添加进原来的列表中。可以查看他们的内存地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = [[] for _ in range(10)]
for i in a:
print(id(i))

[out]:
1999785117192
1999785222472
1999785222344
1999785102344
1999784963272
1999785222536
1999785222600
1999785222792
1999785222856
1999785222728

可以看到,这种方式创建出来的列表的地址空间是完全不一样的,结构如下:

image-20210422151109215
image-20210422151109215

当然,如果采用循环对同一个变量进行创建,那么它的地址任然是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
x = []
a = [x for _ in range(10)]
for i in a:
print(id(i))

[out]:
3005231884744
3005231884744
3005231884744
3005231884744
3005231884744
3005231884744
3005231884744
3005231884744
3005231884744
3005231884744
image-20210422151416752
image-20210422151416752