os.walk和glob哪个更快?

47

我正在使用Python在一个大硬盘上进行文件查找。我一直在研究os.walk和glob。通常我使用os.walk,因为我认为它更整洁,速度也更快(对于普通大小的目录)。有没有人对它们两个有过经验,可以说哪一个更有效率?正如我所说,glob似乎更慢,但你可以使用通配符等,而使用walk,则必须过滤结果。以下是查找核心转储文件的示例。

core = re.compile(r"core\.\d*")
for root, dirs, files in os.walk("/path/to/dir/")
    for file in files:
        if core.search(file):
            path = os.path.join(root,file)
            print "Deleting: " + path
            os.remove(path)
或者
for file in iglob("/path/to/dir/core.*")
    print "Deleting: " + file
    os.remove(file)

9
这句话听起来像是过早优化。我浏览了源代码(http://hg.python.org/cpython/file/d01208ba482f/Lib/glob.py 和 http://hg.python.org/cpython/file/d01208ba482f/Lib/os.py)并发现两个函数都依赖于 os.listdiros.isdir,所以我的直觉告诉我你不会在这两种方式中获得太多收益。(但是,正如下面两个答案中指出的那样,os.walk 会递归子目录而 glob.iglob 不会,因此没有可比性)。如果你最终确实遇到了性能问题,请对几种方法进行分析。否则,就写出清晰的代码即可。 - Steven Rumbalski
5个回答

50

我对1000个目录中的一个小缓存网页进行了研究。任务是计算目录中文件的总数。输出结果如下:

os.listdir: 0.7268s, 1326786 files found
os.walk: 3.6592s, 1326787 files found
glob.glob: 2.0133s, 1326786 files found

正如您所见,os.listdir 是这三种方法中最快的。而对于此任务,glob.glob 仍然比 os.walk 更快。

源代码:

import os, time, glob

n, t = 0, time.time()
for i in range(1000):
    n += len(os.listdir("./%d" % i))
t = time.time() - t
print "os.listdir: %.4fs, %d files found" % (t, n)

n, t = 0, time.time()
for root, dirs, files in os.walk("./"):
    for file in files:
        n += 1
t = time.time() - t
print "os.walk: %.4fs, %d files found" % (t, n)

n, t = 0, time.time()
for i in range(1000):
    n += len(glob.glob("./%d/*" % i))
t = time.time() - t
print "glob.glob: %.4fs, %d files found" % (t, n)

5
os.walk是懒惰的生成器,而glob会在内存中创建一个大列表,对吗? - CMCDragonkai
15
这并不会递归地遍历文件树。 - episodeyang
2
glob.iglob 会返回一个生成器,在 Python 2 中是 https://docs.python.org/2/library/glob.html#glob.iglob,在 Python 3 中是 https://docs.python.org/3/library/glob.html#glob.iglob。 - ghukill
1
这个问题在Python 3.5+中已经被修复,如此处所述:https://docs.python.org/3/library/os.html#os.walk 该函数现在调用os.scandir()而不是os.listdir(),通过减少对os.stat()的调用次数使其更快。 - suvigyavijay

2
你可以使用os.walk,同时仍然使用类似glob的匹配方式。
for root, dirs, files in os.walk(DIRECTORY):
    for file in files:
        if glob.fnmatch.fnmatch(file, PATTERN):
            print file

不确定速度,但显然由于os.walk是递归的,它们执行不同的操作。


0

在比较os.walk和其他方法时往往没有提到的部分是它具有不同的功能。Scandir是最快的实现,仅用于获取目录内容。 Listdir用于简单列出目录内容。 Glob是一个可能递归的模式匹配操作。 Walk是对目录的递归遍历,用户完全控制。

  • 用户可以通过设置topdown=True|False来决定遍历顺序
  • 用户可以在遍历过程中修剪树(使用topdown=True),从给定目录的子目录列表中删除目录
  • 用户可以在遍历过程中对文件和目录执行任意操作。
  • 通过组合遍历顺序和文件可移除性的控制,用户可以使用topdown=False先删除文件,然后如果目录为空,则删除该目录。

walk作为通用工具的强大之处也有代价。如果你只需要listdir或glob,那么应该使用它们而不是walk。但是,当你需要walk的功能时,其他方法就无法胜任。


0

*, ?, 和使用 [] 表示的字符范围将被正确匹配。这是通过使用 os.listdir() 和 fnmatch.fnmatch() 函数实现的。

我认为即使使用 glob,你仍然需要使用 os.walk,除非你直接知道子目录树的深度。

顺便说一下,在 glob 文档 中写道:

"*, ?, 和使用 [] 表示的字符范围将被正确匹配。这是通过使用 os.listdir() 和 fnmatch.fnmatch() 函数实现的。"

我会简单地使用

for path, subdirs, files in os.walk(path):
        for name in fnmatch.filter(files, search_str):
            shutil.copy(os.path.join(path,name), dest)

-2

在测量/分析之前不要浪费时间进行优化。专注于使您的代码简单易维护。

例如,在您的代码中,您预编译了RE,但这并没有给您带来任何速度提升,因为re模块具有预编译的RE的内部re._cache

  1. 保持简单
  2. 如果速度慢,则进行分析
  3. 一旦您确切知道需要优化什么,请进行一些微调,并始终记录它

请注意,几年前进行的某些优化可能会使代码运行速度比“未经优化”的代码更慢。这尤其适用于现代基于JIT的语言。


48
原文:OP提到了一个“大盘”,而且代码显然已经很简单了。此外,OP似乎正在优化阶段。在SO上用“过早优化是万恶之源”的说法来抛弃与性能相关的问题是一种灾难(实际上这是Knuth的误引)。翻译:原作者提到了一个“large disk”(大型磁盘), 代码已经很简单了。此外,原作者似乎处于优化阶段。在Stack Overflow上,使用“premature optimization is the root of all evil”(过早优化是万恶之源)这样的话来回避与性能相关的问题是一种糟糕的做法(实际上这是 Knuth 的被误引用的话)。 - kgadek
26
在现实世界(职业领域)中,优化非常重要,因为事情往往规模非常大。不要毫无理性地盲目反对优化。 - Jules G.M.
1
过早地进行优化是愚蠢的。它几乎总是使代码更难维护,有时甚至会导致性能变差。我不是说这一定是这种情况,但可能存在这种情况。 - Michał Šrajer
2
这里没有意义。胡说八道。当然,优化在这里很重要。 - ABCD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接