Python 生成只有重复元素(本例此重复元是空 list)的列表有两种方式:
- 乘法(
*
)生成:[[]] * 3
- 循环(
for
)生成:[[] for _ in range(3)]
这可能在初始化一个用以记录结果的嵌套数组时用到。两种方法生成的初始空数组看似一样,效果却不同!
- 前者内层的 3 个 list 其实是同一个(python 变量全是引用 / 一级指针,指向同一个),会互相影响;
- 后者内层则是 3 个独立的 list,不会相互影响。
本文同时测试重复元为数时的一组对比:
[0] * 3
,与[0 for _ in range(3)]
Test
- 第一组对比(
a
v.s.b
),list 中 list,内层数组 append 数据; - 第二组对比(
c
v.s.d
),list 中 int,对数做加法。
def inspect(arr, name):
"""看数组各元素的值、地址"""
for i in range(len(arr)):
print(f"{name}[{i}]:", arr[i], id(arr[i]))
a = [[]] * 3
print("init a:")
inspect(a, 'a')
for _ in range(4):
a[0].append(1) # 只往内层第一个 list append 数据
print("after appending:")
inspect(a, 'a')
b = [[] for _ in range(3)]
print("init b:")
inspect(b, 'b')
for _ in range(4):
b[0].append(1) # 只往内层第一个 list append 数据
print("after appending:")
inspect(b, 'b')
c = [0] * 3
print("init c:")
inspect(c, 'c')
for _ in range(4):
c[0] += 1 # 只对第一个数 +1
print("after adding:")
inspect(c, 'c')
d = [0 for _ in range(3)]
print("init d:")
inspect(d, 'd')
for _ in range(4):
d[0] += 1 # 只对第一个数 +1
print("after adding:")
inspect(d, 'd')
输出:
init a:
a[0]: [] 2191561740608
a[1]: [] 2191561740608
a[2]: [] 2191561740608
after appending:
a[0]: [1, 1, 1, 1] 2191561740608
a[1]: [1, 1, 1, 1] 2191561740608
a[2]: [1, 1, 1, 1] 2191561740608
init b:
b[0]: [] 2191563920704
b[1]: [] 2191563924480
b[2]: [] 2191563920960
after appending:
b[0]: [1, 1, 1, 1] 2191563920704
b[1]: [] 2191563924480
b[2]: [] 2191563920960
init c:
c[0]: 0 140710718640904
c[1]: 0 140710718640904
c[2]: 0 140710718640904
after adding:
c[0]: 4 140710718641032
c[1]: 0 140710718640904
c[2]: 0 140710718640904
init d:
d[0]: 0 140710718640904
d[1]: 0 140710718640904
d[2]: 0 140710718640904
after adding:
d[0]: 4 140710718641032
d[1]: 0 140710718640904
d[2]: 0 140710718640904
Discussion
两组对比初始化后,效果都看似相同,然而看地址就能发现并不是。
第一组 list 中 list:a
只让 a[0]
append 数据,append 完 a[1]
、a[2]
也多了;而 b
同样只让 b[0]
append,不影响 b[1]
、b[2]
。再对比内层 list 的地址可知,*
生成的 a[0~2]
其实都指向同一个 list 对象,而 for
生成的 b[0~2]
则指向 3 个独立 list 对象。
第二组 list 中 int:无论 *
还是 for
生成,对第一维加数都不影响第二、三维。哦? 第二组对数操作的机制与第一组对 list 操作的机制不同,由地址可见所有常数 0
的地址都相同,即初始的 c[0~2]
都指向同一个 0
(这与第一组类似),而初始的 d[0~2]
也指向同一个 0
,且是与 c[0~2]
同一个,参考 [1,2]。而对第一维加数只会让 c[0]
、d[0]
改指向另一个常数(本例最终都指向同一个 4
),并不是像 C 一样改值,而又不影响 c[1~2]
、d[1~2]
的指向,所以它们不受影响。
Conclusion
所以,list 中 list 无脑用 for
生成,避免副作用;list 中 int 则两种都行。