- Effective Python:编写高质量Python代码的90个有效方法(原书第2版)
- (美)布雷特·斯拉特金
- 1008字
- 2021-08-13 17:10:34
第8条 用zip函数同时遍历两个迭代器
写Python代码时,经常会根据某份列表中的对象创建许多与这份列表有关的新列表。下面这样的列表推导机制,可以把表达式运用到源列表的每个元素上面,从而生成一份派生列表(参见第27条)。
![045-01](https://epubservercos.yuewen.com/873E3D/20818200901954706/epubprivate/OEBPS/Images/045-01.jpg?sign=1739350359-uJPl30vZYUv0reiGySdnSwP11zN9Ma6E-0-4046cea1988b4f3ffaeb359f64d7e8f4)
派生列表中的元素与源列表中对应位置上面的元素有着一定的关系。如果想同时遍历这两份列表,那可以根据源列表的长度做迭代。
![045-02](https://epubservercos.yuewen.com/873E3D/20818200901954706/epubprivate/OEBPS/Images/045-02.jpg?sign=1739350359-GHCJoxNclx56uPgpCEw5wEdc7WEL0ldt-0-5097e7af210fbc6b8a53927abb5a05b8)
这种写法的问题在于,整个循环代码看起来很乱。我们要通过下标访问names
与counts
这两个列表里的元素,所以表示下标的那个循环变量i
在循环体里必须出现两次,这让代码变得不太好懂。改用enumerate
实现(参见第7条)会稍微好一些,但仍然不够理想。
![045-03](https://epubservercos.yuewen.com/873E3D/20818200901954706/epubprivate/OEBPS/Images/045-03.jpg?sign=1739350359-rR2UrIOZKJac5WQLvJPyOh8EM8GJQ5KX-0-6537c4f426de8faaa1b3e9d1473fe68a)
为了把代码写得更清楚,可以用Python内置的zip
函数来实现。这个函数能把两个或更多的iterator封装成惰性生成器(lazy generator)。每次循环时,它会分别从这些迭代器里获取各自的下一个元素,并把这些值放在一个元组里面。而这个元组可以拆分到for
语句里的那些变量之中(参见第6条)。这样写出来的代码,比通过下标访问多个列表的那种代码要清晰得多。
![045-04](https://epubservercos.yuewen.com/873E3D/20818200901954706/epubprivate/OEBPS/Images/045-04.jpg?sign=1739350359-eb76fa5BU3ZY5f907z3UTCpYgwvY45CP-0-f46b57e3b49402131ff899ee4d2db286)
zip
每次只从它封装的那些迭代器里面各自取出一个元素,所以即便源列表很长,程序也不会因为占用内存过多而崩溃。
但是,如果输入zip
的那些列表的长度不一致,那就得小心了。例如,我给names
列表里又添加了一个名字,但是忘了把它的长度更新到counts
列表之中。在这种情况下,用zip
同时遍历这两份列表,会产生奇怪的结果。
![046-01](https://epubservercos.yuewen.com/873E3D/20818200901954706/epubprivate/OEBPS/Images/046-01.jpg?sign=1739350359-TWT8SD5eYKCUHykgeVCNqNztJV2ib6Dd-0-5493e4f9c5ec2e6f8491562eaf06fb63)
新添加的那个'Rosalind'
元素为什么没有打印出来呢?因为zip
函数本来就是这样设计的:只要其中任何一个迭代器处理完毕,它就不再往下走了。于是,循环的次数实际上等于最短的那份列表所具备的长度。一般情况下,我们都是根据某份列表推导出其他几份列表,然后把这些列表一起封装到zip
里面,由于这些列表长度相同,因此不会遇到刚才的问题。
在列表长度不同的情况下,zip
函数的提前终止行为可能跟你想实现的效果不一样。所以,如果无法确定这些列表的长度相同,那就不要把它们传给zip
,而是应该传给另一个叫作zip_longest
的函数,这个函数位于内置的itertools
模块里。
![046-02](https://epubservercos.yuewen.com/873E3D/20818200901954706/epubprivate/OEBPS/Images/046-02.jpg?sign=1739350359-cAHcPcF554iyRa6U0hmsraxmGVWu1pDh-0-5cee52cf0d47f75bb8fc6a21295279cd)
如果其中有些列表已经遍历完了,那么zip_longest
会用当初传给fillvalue
参数的那个值来填补空缺(本例中空缺的为字符串'Rosalind'
的长度值),默认的参数值是None
。
要点
- 内置的
zip
函数可以同时遍历多个迭代器。 zip
会创建惰性生成器,让它每次只生成一个元组,所以无论输入的数据有多长,它都是一个一个处理的。- 如果提供的迭代器的长度不一致,那么只要其中任何一个迭代完毕,
zip
就会停止。 - 如果想按最长的那个迭代器来遍历,那就改用内置的
itertools
模块中的zip_longest
函数。