python - 自写的 os.walk-alike 比 os.walk 本身慢得多 - 为什么?
问题描述
不幸的是,这段代码的运行速度比“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)
解决方案
os.walk()
不使用os.listdir()
。它使用了更快的os.scandir()
函数,它为每个目录条目提供了一个迭代器更多的信息:
使用
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
异常,您的毯子 Pokemon-catch-em-all 除了处理程序静音。
接下来,您应该摆脱全局变量,并使用正确的变量名称。files
anddirs
将比y
and更清晰的名称z
。因为您创建了全局变量y
,所以z
您保留了给定级别的所有文件和目录名称,并且对于向下的每个第一个子目录,您然后重新报告这些相同的文件和目录名称,就好像它们是这些子目录的成员一样。只有当到达这样一个目录树的第一个叶子(没有更多的子目录)时,才会.clear()
调用y
并z
执行,导致重复文件名的结果非常混乱。
你可以研究os.walk()
源码,但是如果我们把它简化为只使用自上而下的遍历而没有错误处理,那么它归结为:
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
变量被重新用于递归到子目录中。
推荐阅读
- node.js - 渲染两个单独的 async.each 方法的结果
- java - JavaFX 通过 setOnKeyPressed 更新当前位置
- c# - Xamarin Forms CollectionView 绑定后保持为空
- java - 在复合键上使用“链式”外键时出现问题
- python - 过滤 Div,BeautifulSoup,带空返回
- android - 从与帐户触发器关联的联系人的意图中提取联系人信息
- php - 新的 WP_REST_Request(PUT)
- python - 尝试 MNIST 数据集时出现 tensorflow 和 matplotlib 包的问题
- protocol-buffers - .Net Framework/C# - --grpc_out:protoc-gen-grpc:访问被拒绝问题
- php - Symfony 5 缓存:清除 php.CRITICAL:致命错误:允许的内存大小