一文学完Python内置的zip()zip()是Python中最有用的内置类型之一,它可以接受多个iterable对象参数,并返回一个迭代器,该迭代器可以组合不同可迭代对象的元素。之前写迭代器系列的时候,在《Python进阶:设计模式之迭代器模式》里简单介绍过。前几天翻译了Python3.10采用的PEP-618,介绍了它将迎来的变化。但是还是有很多同学不知道zip(),或者不能熟练掌握它的用法,所以本文意在进行更详细的审查。内容主要分为三个部分:使用部分:介绍其基本用法、高级用法、展示操作用法高级部分:介绍其实现原理,注意几个实现细节发散部分:重点介绍其不足之处,以及解决方案1.nusagesofzip()基本用法:像拉链一样,组合多个可迭代对象,然后用for循环将它们一个一个取出来,或者将结果一次存放到列表、元组、字典等容器中时间。它的结果是一个迭代器,迭代器生成的元素是元组,第i个元组的元素来自可迭代对象参数的第i个元素,如上图所示。另外,for循环还可以依次取出元组中的元素,非常方便:它的参数不需要是同类型的可迭代对象,所以可以有很多种组合,例如:但是,如果字典作为zip()的参数,结果会是什么?字典是key-value键值对的形式,不同于列表等单元素结构。实验一下,可以看出zip()默认只会遍历字典的key值:如果要取出字典的value值,或者取出key-value键值对,那么就可以使用字典自带的遍历方法values()和items():使用zip(),实现二维列表的行列转换也很方便:上例中的星号(*)运算符即可unpacked(解包),即将my_list(也是一个列表)的元素解包成多个参数给zip(),重新组合3个列表。解包操作符同样适用于zip对象,因为zip()本身就是一个行列转换操作。如果解包后作为参数传递给zip(),相当于又做了一次行列转换,即回到原点(除了最后的结果是一个元组):最后再介绍一个用法:创建一个n*n的方阵,每一行的数字都相同。2、zip()原理分析官方文档给出了zip()的Python伪代码(不是Python解释器内置的实现,只是展示基本的代码逻辑):defzip(*iterables):#zip('ABCD','xy')-->AxBysentinel=object()iterators=[iter(it)foritiniterables]whileiterators:result=[]foritiniterators:elem=next(it,sentinel)ifelemissentinel:returnresult.append(elem)yieldtuple(result)在这段简短的代码中,可以分析出一些关键信息:zip接收可变数量的可迭代对象参数,这些参数将通过iter传递()被处理成一个迭代器。推论:如果存在不可迭代对象,这里会报错。while循环是判断列表是否为空,列表中的元素是参数转换而来的迭代器。推论:如果输入参数中存在有效的可迭代对象,则while循环始终为真;如果没有输入参数,则什么也不做。next()会依次读取迭代器中的下一个元素,它的第二个参数会作为迭代器耗尽时的返回值。推论:每一轮依次取出这些迭代器中的一个元素。当一次迭代耗尽时,它会退出死循环,也就是说未耗尽的迭代器会被直接丢弃。3.zip()的问题zip()最明显的问题是它丢弃了未用尽的迭代器:这是一个桶效应,最终结果由最短的木板决定。一种解决方案是拿长板补短板(填充None值),就是itertools中的zip_longest方法:在最大程度保证原始数据完整性的同时,填充冗余数据。但是,如果我们不想要冗余数据,而只想要最长对齐的数据怎么办?Python官方最近采用了PEP-618,解决了这个问题。当迭代器的长度不一致时,既不折衷短板,也不折衷长板,而是抛出ValueError。它认为输入参数值错误,即严格要求输入参数的数据完整性。PEP将在一年后合并到Python3.10版本中。有关详细信息,请参阅此PEP-618翻译。公众号:蟒猫头条号:蟒猫知乎:猫下豌豆花掘金:猫下豌豆花
