python生成列表坑

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 则两种都行。

References

  1. 谈谈Python的常量池
  2. Are strings pooled in Python?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值