在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, οnerrοr=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 的功能。