编写高质量Python(第8条) 用zip函数同时遍历两个迭代器

第8条 用zip函数同时遍历两个迭代器

​ 写 Python 代码时,经常会根据某份列表中的对象创建许多与这份列表有关的新列表。下面这样的列表推导机制,可以把表达式运用到源列表的每个元素上,从而生成一份派生列表(参见第27条)。

names = ['Cecilia', 'Lisa', 'Marie']
counts = [len(n) for n in names]
print(counts)

>>>
[7, 4, 5]

​ 派生列表中的元素与源列表中对应位置上面的元素有着一定的关系。如果想同时遍历这两份列表,那可以根据源列表的长度做迭代。

longest_name = None
max_count = 0
for i in range(len(names)):
    count = counts[i]
    if count > max_count:
        longest_name = names[i]
        max_count = count
print(longest_name)

>>>
Cecilia

​ 这种写法的问题在于,整个循环代码看起来很乱。我们要通过下标访问 names 和 counts 这两个列表里面的元素。所以表示下标的那个循环变量 i 在循环体必须出现两次,这让代码变得不太好懂。该用 enumerate 实现(参见 第7条 )会变得更好一些,但仍然不够理想。

for i, name in enumerate(names):
    count = counts[i]
    if count > max_count:
        longest_name = name
        max_count = count

​ 为了将代码写清楚,可以用 Python 内置的 zip 函数来实现。这个函数能把两个或更多的 iterator 封装成惰性生成器(lazy generator)。每次循环时,它会分别从这些迭代器里获取各自的下一个元素,并把这些值放在一个元组里面。 而这个元组可以拆分到 for 语句里的那些变量之中(参见 第6条)。这样写出来的代码,比通过下标访问多个列表的那种代码清晰得多。

for name, count in zip(names, counts):
    if count > max_count:
        longest_name = name
        max_count = count

​ zip 每次只从它封装的那些迭代器里面各取一个元素,所以即使源列表很长,程序也不会因为占用内存过多而崩溃。

​ 但是,如果输入 zip 的那些列表长度不一致,那就得小心了。例如,我给 names 表里面又添加了一个名字,但是忘了把它的长度更新到 counts 列表之中。在这种情况下,用 zip 同时遍历这两份列表,会产生奇怪的结果。

names.append('Rosalind')
for name, count in zip(names, counts):
    print(name)
    
>>>
Cecilia
Lisa
Marie

​ 新添加的那个 ‘Rosalind’ 元素为什么没有打印出来呢?因为 zip 函数本来就是这样设计的:只要其中任何一个迭代器处理完毕,它就不往下面走了。于是,循环的次数实际上等于最短的那份列表所具备的长度。一般情况下,我们都是根据某份列表推导出其他几份列表。然后把这些列表一起封装到 zip 里面,由于这些列表长度相同,因此不会遇到刚才到问题。

在列表长度不同的情况下,zip 函数的提前终止行为可能跟你想实现的效果不一样。所以,如果无法确定这些列表的长度相同,那就不要把它们传给 zip,而是应该传给另一个 zip_longst ,这个函数位于 itertools 模块里。

import itertools
for name, count in itertools.zip_longest(names, counts):
    print(f'{name}: {count}')

>>>
Cecilia: 7
Lisa: 4
Marie: 5
Rosalind: None

​ 如果其中有些列表已经遍历完了,那么 zip_longest 会用当初传给 fillvalue 参数的那个值来填补空缺(本例子中空缺的字段为字符串 ‘Rosalind’ 的长度值),默认的参数值为 None.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值