python列表反序后为什么结果是nonf_[Python]为何要用列表推导式?

本文解析了Python中使用列表乘法操作可能遇到的问题,特别是对于二维列表时,如何避免因引用拷贝导致的数据意外变动,并对比了列表推导式的正确用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我的回答有什么行为习惯昭示着你是个编程大佬?​www.zhihu.com

中, @霍华德 师兄好奇为什么不要写

[0] * N

于是我便想结合Python的雷区,来解释一下。

什么是列表推导式?

列表推导式

index = [_ for _ in range(N)]

就是列表推导式,常用于代替循环,来创建规律性数组,比如[0, 1, 2, 3,...],属于Python的语法糖。那为什么 [0]* N 不太好呢?其实 [0]* N 跟 [0 for _ in range(N)] 完全等价,前者还更简洁啊。下面来说明原因。

基本数据类型和引用式拷贝

众所周知,Python中的所有数据都是对象,包括0, 1.0这样的整数or浮点数常量。然后Python的内存管理我也不太懂,就不深入了。只需要知道,0,1这样的整数基本单元(表现起来)是直接拷贝的。这是基本数据类型。

但是非基本数据类型,比如列表、字典,如果不调用copy()或者deepcopy(),那诸如使用 list2=list1, dict2=dict1的赋值式拷贝,统统都是传引用。也就是说,它们共享内存,改变list1/dict1中的元素,list2/dict2中的元素也一定会跟着改变。

好了,熟悉Python的人一定知道这些了,那么这个跟列表乘法操作和列表推导式有什么关系呢?下面来简单解释一下。

列表乘法操作和列表推导式

列表乘法操作并不是拷贝被乘的列表,而是拷贝了它的引用。

对于一维列表则没有雷区,如 [0, 1] * 3, 结果为 [0, 1, 0, 1, 0, 1],每个元素依然是独立的。

但是对于二维列表,雷区来啦ψ(`∇´)ψ!上代码:

def list_comp_test1():

"""list comprehension test function 1"""

N = 3

list1 = [0] * N

print(f"list1: {list1}")

# modify the first element

list1[0] = 1

print(f"list1: {list1}")

def list_comp_test2():

"""list comprehension test function 2"""

N = 3

list2 = [[0, 1]] * N # will copy by reference

print(f"list2: {list2}")

# modify the first element

list2[0][0] = 1

print(f"list2: {list2}")

def list_comp_test3():

"""list comprehension test function 2"""

N = 3

list3 = [[0, 1] for _ in range(N)] # will copy by truly copy

print(f"list3: {list3}")

# modify the first element

list3[0][0] = 1

print(f"list3: {list3}")

def main():

print("Now testing list multiply operation...")

list_comp_test2()

print("Now testing list comprehension...")

list_comp_test3()

if __name__ == "__main__":

main()

输出:

Now testing list multiply operation...

list2: [[0, 1], [0, 1], [0, 1]]

list2: [[1, 1], [1, 1], [1, 1]]

Now testing list comprehension...

list3: [[0, 1], [0, 1], [0, 1]]

list3: [[1, 1], [0, 1], [0, 1]]

注意差别,使用了列表乘法操作的list2,用 list2[0][0]=1 改变它的第一个元素的第一个元素,结果list2中的其余元素均被改变了。而使用列表推导式生成的list3,则没有这个问题!

这是因为list2中的每个元素(都是列表)拷贝的都是引用!而list3,由于是列表推导式,它的每个元素都是独立计算而成,占用独立的内存!

什么时候会踩雷

刷leetcode的同学一定会被坑过。比如创建一个最简单的图的邻接矩阵的时候(现在都用字典作为hash表来写了):

>>> a = [0, 0, 0, 0, 0, 0]

>>> a = [a] * 3

>>> a

[[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]

就会哦豁完蛋ψ(`∇´)ψ

>>> a[0][0] = 1

>>> a

[[1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0]]

结论

故想放心地生成元素在内存上独立的数组,还是用列表生成式比较好。

撰写仓促,很多概念的参考资料就不一一列举了,感兴趣的同学可以一起看Python文档。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值