Python中经常会和list打交道。例如从一个list中产生相关list(list comprehensions)。
names = ['Cecilia', 'Lise', 'Marie']
counts = [len(n) for n in names]
print(counts)
>>>
[7, 4, 5]
想遍历两个列表,可以使用range+index形式:
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
显然这种方式太冗长,我们在Item 7中说过。考虑使用enumerate:
for i, name in enumerate(names):
count = counts[i]
if count > max_count:
longest_name = name
max_count = count
效果还是不够理想(索引方式在作者看来比较糟糕)。
为了让代码更干净,Pyhon提供了内置的zip函数。zip用一个惰性生成器来包装两个或更多迭代器。zip generator从迭代器中产生tuples。这些tuple可以在for中直接被解包。
for name, count in zip(names, counts):
if count > max_count:
longest_name = name
max_count = count
但是,要注意当迭代器长度不同时,zip会产生意想不到的结果。例如,names增加了一个元素,但是counts没有相应增加:
names.append('Rosalind')
for name, count in zip(names, counts):
print(name)
>>>
Cecilia
Lise
Marie
names中新加的'Rosalind'没有出现。这就是zip的工作方式(truncating),当较短的迭代器耗尽后,zip也就结束了。
如果你传递的迭代器长度不同,可以考虑zip_longest
来替代zip。
import itertools
for name, count in itertools.zip_longest(names, counts):
print(f'{name}: {count}')
>>>
Cecilia: 7
Lise: 4
Marie: 5
Rosalind: None
zip_longer
会将缺失值替换为fillvalue,默认是None。
zip_longest
可以解决上面的截断行为。