自写类似os.walk的速度要比os.walk本身慢得多-为什么?

时间:2019-01-28 12:39:54

标签: python python-3.x performance python-3.6

不幸的是,这段代码的运行速度比“ os.walk”慢,但是为什么呢?

是否可能是“ for”周期导致其运行缓慢?

”类似于“ os.walk”的代码:(“ os.walk”功能可以完成此工作)

注意: 我写信是为了提高自己!

import os, time
from os.path import *

x = ""
y = []
z = []
var = 0

def walk(xew):
    global top, var, x,y,z
    if not var: var = [xew]
    for i in var:
        try:
            for ii in os.listdir(i):
                y.append(ii) if isdir(i+os.sep+ii) else z.append(ii)

            x = top = i
            var = [top+os.sep+i for i in os.listdir(top) if isdir(top+os.sep+i)]         
        except:
            continue
        yield x,y,z
        yield from walk(var)
        var.clear();y.clear();z.clear()

例如

它在2秒内结束:

for x,y,z in walk(path):
    print(x)

在0.5秒内:

for x,y,z in os.walk(path):
    print(x)

2 个答案:

答案 0 :(得分:4)

os.walk()不使用os.listdir()。它使用速度更快的os.scandir() function,它为迭代器的每个目录条目提供了更多信息:

  

使用scandir()代替listdir()可以显着提高还需要文件类型或文件属性信息的代码的性能,因为如果操作系统在以下情况下提供os.DirEntry对象,则它们会公开此信息:扫描目录。所有os.DirEntry方法都可以执行系统调用,但是is_dir()is_file()通常只需要系统调用即可进行符号链接; os.DirEntry.stat()在Unix上始终需要系统调用,而在Windows上仅需要一个系统调用即可。

os.walk()代码大量使用了DirEntry.is_dir()调用,而使用os.scandir()则比使用os.isdir()便宜得多(后者必须进行单独的os.stat()调用) )。

接下来,您的代码经常调用os.isdir()。您实际上对路径中的每个文件条目都调用了两次。您已经收集了y中的所有子目录,并且在重新创建var时不需要再次测试路径。这些额外的isdir()调用花费了您很多时间。

var为空(没有其他子目录)时,您也会递归,导致您先将空列表包装在另一个列表中,然后os.listdir()会抛出{{ 1}}例外,您的一揽子神奇宝贝-捉住他们-除了处理程序无声外,其他所有内容都没有。

接下来,您应该摆脱全局变量,并使用适当的变量名称。 TypeErrorfiles的名称比dirsy更清楚。因为您设置了zy全局变量,所以您将保留给定级别的所有文件和目录名称,并且对于向下的每个第一个子目录,然后重新报告这些相同的文件和目录名称,就好像它们是这些子目录的成员。仅当到达此类目录树的第一个叶子(没有其他子目录)时,才会执行对z.clear()的{​​{1}}调用,从而导致文件名重复,结果非常混乱。

您可以研究os.walk() source code,但是如果我们将其简化为仅使用自上而下的遍历并且不进行错误处理,则可以归结为:

y

请注意,没有未使用全局变量;该算法根本不需要任何算法。每个目录只有一个z调用,并且def walk(top): dirs = [] nondirs = [] with os.scandir(top) as scandir_it: for entry in scandir_it: if entry.is_dir(): dirs.append(entry.name) else: nondirs.append(entry.name) yield top, dirs, nondirs for dirname in dirs: new_path = os.path.join(top, dirname) yield from walk(new_path) 变量被重新使用以递归到子目录中。

答案 1 :(得分:0)

此代码的运行速度几乎与os.walk一样!

import os, time
from os.path import *

def walk(top):
    x = top;y=[];z=[]
    try:
        for i in os.listdir(top):
            y.append(i) if isdir(top+os.sep+i) else z.append(i)
    except: pass
    else:
        yield x,y,z
        for q in y: yield from walk(top+os.sep+q)