首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >自写的os.walk比os.walk本身慢得多,为什么?

自写的os.walk比os.walk本身慢得多,为什么?
EN

Stack Overflow用户
提问于 2019-01-28 12:39:53
回答 2查看 247关注 0票数 3

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

会不会是"for“循环导致它运行缓慢?

“代码”,类似于'os.walk': ( "os.walk“函数做它所做的事情)

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

代码语言:javascript
复制
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秒内结束:

代码语言:javascript
复制
for x,y,z in walk(path):
    print(x)

在0.5秒内:

代码语言:javascript
复制
for x,y,z in os.walk(path):
    print(x)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-01-28 12:45:46

os.walk()不使用os.listdir()。它使用速度快得多的 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()抛出一个TypeError异常,除了处理程序的静默之外,所有这些都会引发TypeError异常。

接下来,您应该去掉全局变量,并使用适当的变量名。filesdirs的名字比yz要清晰得多。因为您创建了yz全局文件,所以您将保留给定级别的所有文件和目录名称,并且对于每个第一个子目录,然后重新报告相同的文件和目录名称,就好像它们是这些子目录的成员一样。只有当到达这样一个目录树的第一个叶(没有更多的子目录)时,才会执行对yzz调用,从而导致重复文件名带来非常混乱的结果。

您可以学习 source code,但是如果我们将其简化为只使用自顶向下的遍历而不使用错误处理,则可以归结为:

代码语言:javascript
复制
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)

请注意,没有使用全局变量;在此算法中根本不需要任何变量。每个目录只有一个os.scandir()调用,dirs变量被重新用于递归到子目录。

票数 5
EN

Stack Overflow用户

发布于 2019-01-28 22:22:09

这段代码的工作速度几乎和os.walk一样快!

代码语言:javascript
复制
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)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54402246

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档