在Python中,文件遍历是一件非常轻松简便的工作。官方给出的示例:
import os from os.path import join, getsize for root, dirs, files in os.walk('python/Lib/email'): print root, "consumes", print sum([getsize(join(root, name)) for name in files]), print "bytes in", len(files), "non-directory files" if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories
可以看到,基本上就是依赖os.walk()这个函数来实现的。从结构上来看,for root, dirs, files in os.walk(...),很容易让人认为os.walk(...)生成了一个迭代器。迭代器的next方法可能会返回下一层次的文件夹内容。事实上,os.walk()是一个生成器函数。生成器与迭代器,是Python引入的几大特性之一,而生成器要比迭代器高级一些。至于生成器的工作原理和适用场合,可以先从os.walk()的源码说起。
os.walk()源码:
def walk(top, topdown=True, onerror=None, followlinks=False): from os.path import join, isdir, islink try: # Note that listdir and error are globals in this module due # to earlier import-*. names = listdir(top) except error, err: if onerror is not None: onerror(err) return dirs, nondirs = [], [] for name in names: if isdir(join(top, name)): dirs.append(name) else: nondirs.append(name) if topdown: yield top, dirs, nondirs for name in dirs: path = join(top, name) if followlinks or not islink(path): for x in walk(path, topdown, onerror, followlinks): yield x if not topdown: yield top, dirs, nondirs
实现的原理很简单,首先列出top文件夹下所有的文件(夹)的名字,names,然后遍历每一个name,来判断name是文件夹还是文件,分别放到不同的列表中。这时,yield语句出现了,这正是生成器的魔法所在。任何包含yield语句的函数,都被称作是生成器函数。如何理解这个表达式呢,可以把它看做是和return一样的功效,即让函数返回结果。事实上,yield并不是单纯的return,它将top,dirs,noddirs返回后,就冻结了,就是说,这个函数不会再次执行。那什么时候会恢复执行呢,就是当你再次去调用walk方法的时候,此时,函数被激活,继续执行。
yield并没有太大特别之处,只不过它能够使函数的执行被冻结,并且能够被激活再次进入运行状态。那么,os.walk()就不再神秘了。再次进入运行态后,就会进入 递归调用了,即 for x in walk(path.....): yield x 的功能。