4.1.6 跳过可迭代对象的开始部分

在实际应用中,我们会有类似跳过可迭代对象的需求。

itertools模块中有一些函数可以实现这个需求,如itertools.dropwhile()函数。该函数需要传递一个函数对象和一个可迭代对象,返回一个迭代器对象,丢弃直到函数返回Flase之前的原有序列中的所有元素,然后返回后面的所有元素。

如读取一个开始部分是几行注释的源文件,代码如下:


with open('/etc/passwd') as f:
    for line in f:
        print(f'{line}', end='')

如跳过开始部分的注释行,代码如下:


from itertools import dropwhile
with open('/etc/passwd') as f:
    for line in dropwhile(lambda line: line.startswith('#'), f):
        print(f'{line}', end='')

如果已经明确知道要跳过的元素的个数,可以使用itertools.islice()函数来代替上述代码,代码(iter_skip.py)示例如下:


from itertools import islice
items = ['w', 'o', 'r', 12, 5, 7, 90]
for x in islice(items, 3, None):
    print(x)

执行py文件,输出结果如下:


12
5
7
90

示例中,islice()函数最后的None参数指定了获取items中从第3个到最后的所有元素,如果None和3的位置对调,意思就是仅仅获取前3个元素(这与切片的相反操作[3:]和[:3]原理是一样的)。

dropwhile()和islice()是两个帮助函数,作用是避免写出类似如下的冗余代码:


with open('/etc/passwd') as f:
    # Skip over initial comments
    while True:
        line = next(f, '')
        if not line.startswith('#'):
            break

    # Process remaining lines
    while line:
        # Replace with useful processing
        print(line, end='')
        line = next(f, None)

跳过一个可迭代对象的开始部分与过滤是不同的。如上述代码的第一个部分可能会这样重写:


with open('/etc/passwd') as f:
    lines = (line for line in f if not line.startswith('#'))
    for line in lines:
        print(line, end='')

这样写确实可以跳过开始部分的注释行,但是同样会跳过文件中其他的注释行。

注意 这里的方案适用于所有可迭代对象,包括那些事先不能确定大小,如生成器、文件及其他类似的对象。