当前位置: 首页 > 科技观察

序列赋值导致的Python列表卡住

时间:2023-03-22 16:24:52 科技观察

本文转载自微信公众号“东方儿”,作者东方儿。转载本文请联系东方儿公众号。顺序赋值是Python的默认操作。如果使用不当,可能会陷入语法陷阱。++表示将两个序列的元素连接在一起。通常+号两边的序列是由同一类型的数据组成的。在拼接过程中,两个被操作的序列不会被修改,Python会创建一个包含与拼接结果相同类型数据的新序列。例如:a=[1]b=[2]c=a+bprint(a,b,c)print(id(a),id(b),id(c))结果为:[1][2][1,2]240961052448024096105235202409610523648*如果想把一个序列复制几份然后拼接在一起,用整数乘以序列会更快。同样,此操作会产生一个新序列:>>>l=[1]>>>l*5[1,1,1,1,1]>>>5*"a"'aaaaa'+和*都遵循这条规则,不修改原来的操作对象,而是建立一个新的序列。列表和列表的陷阱猜猜结果会是什么:x=["x"]my_list=[x]*3print(my_list)#[['x'],['x'],['x']]x2=my_list[2]x2[0]="y"print(my_list)是合理的,应该是[['x'],['x'],['y']],但是错了,它实际上是:[['y'],['y'],['y']]难以置信!分配my_list的最后一个元素的列表会导致分配所有三个元素的列表!这体现了my_list的三个元素,元素不??是3个列表,而是3个列表引用,指向同一个列表。等同于:x=["x"]my_list=[]foriinrange(3):my_list.append(x)#添加相同的对象x2=my_list[2]x2[0]="y"print(my_list)#[['y'],['y'],['y']]每次都将相同的对象附加到my_list。如果要生成3个不同的列表,则需要在每次迭代中创建一个新列表:my_list=[]foriinrange(3):x=["x"]#Newlistmy_list.append(x)x2=my_list[2]x2[0]="y"print(my_list)#[['x'],['x'],['y']]这符合预期。代码可以通过列表理解来简化:x=["x"]my_list=[xforirange(3)]x2=my_list[2]x2[0]="y"print(my_list)#[['x'],['x'],['y']]课程:创建列表列表,使用列表理解,不要使用*运算符。如果在语句a*n中,序列a中的元素是对其他变量对象的引用,需要特别注意,这可能不是你想要的效果。虽然+=a+=b表示a=a+b,但其背后的特殊方法是__iadd__。如果一个类没有实现这个方法,Python将退后一步并调用__add__。__iadd__方法会直接对原对象进行add,__add__方法会先生成一个新的对象,然后再赋值。*=+=的这些概念也适用于*=,但后者对应的是__imul__。Appending仍然是一个新对象,应用于可变序列和不可变序列时效果明显。例子:#可变序列,追加>>>l=[1,2,3]>>>id(l)2135319475136>>>l*=2>>>l[1,2,3,1,2,3]>>>id(l)2135319475136#idsame#immutablesequence,newobject>>>t=(1,2,3)>>>id(t)2135322139520>>>t*=2>>>id(t)2135321695424#Id不相同的元组集合列表>>>t=(1,2,[30,40])>>>t[2]+=[50,60]猜猜下面4种情况中哪种会发生?a.t变为(1,2,[30,40,50,60])b.因为元组不支持对其元素的赋值,所以会抛出TypeError异常c。以上两个都不是d。a和b都是对的,因为元组不能赋值,所以我会毫不犹豫的选择b。但实际上答案是d!a和b都正确,都会赋值成功,报错:>>>t=(1,2,[30,40])>>>t[2]+=[50,60]traceback(最近调用最后):文件“”,第1行,在类型错误:“元组”对象不支持项目分配>>>t(1,2,[30,40,50,60])不好了!为什么?1、赋值成功,因为t[2]指向一个可变对象(list[30,40]),可变对象可以赋值。2.报错,因为可变对象赋值给了不可变对象(元组t),不可变对象无法赋值。写成t[2].extend([50,60])可以避免这个异常。经验教训:不要将可变对象放入元组中。+=不是原子操作,虽然抛出异常,操作还是完成了。这位巴西作者说,在他15年的Python职业生涯中,他还没有看到有人患上这个地方。总结本文介绍了+、*和list-in-list,+=、*=和tuple-in-list的陷阱,并分别吸取教训。这是动态语言的缺点。运行后才能知道是否有类型错误。你只能通过积累代码经验来避免它。鱼和熊掌不可兼得。在享受Python简洁语法带来的便利的同时,也不得不付出麻烦的运行报错和排错的代价。参考资料:《流畅的Python》