如何在Python中仅用一次OS调用获取目录的所有孙子目录
我正在尝试在Python中获取某个目录下的所有孙子目录。因为性能原因,我不想在循环中不断调用操作系统的函数(因为这是一个网络文件系统)。这是我目前的做法。有没有更简单的方法可以做到这一点?
dirTree = os.walk(root)
children = [os.path.join(root, x) for x in dirTree.next()[1]]
grandChildren = []
for root, dirs, files in dirTree:
if root in children:
for dir in dirs:
grandChildren.append(os.path.join(root, dir))
补充说明:我不太清楚我调用的os.walk是否是懒加载的。我希望在调用后整个目录树都能在内存中,但我不确定是否真的这样。
2 个回答
1
在POSIX系统和Windows系统中,你不能通过一次操作就获取所有这些数据。至少在POSIX系统中,每个目录需要进行三次操作(opendir
、readdir
、close
),而每个目录项还需要一次额外的操作(stat
)。
我认为接下来的方法会比你提供的方案减少操作系统的调用次数。是的,os.walk()
的调用是懒惰的;也就是说,调用walk()
时,整个目录树并不会全部加载到内存中,而是通过后续调用next()
逐步读取。
因此,我的方法只会读取第一层子目录,并且只会对直接的子目录和孙目录进行stat
操作。你的方法则会对所有的曾孙目录也进行这样的操作,深度取决于你的目录结构有多复杂。
root='.'
grandChildren = []
for kid in next(os.walk('.'))[1]:
x = next(os.walk(os.path.join('.', kid)))
for grandKid in x[1]: # (or x[1]+x[2] if you care about regular files)
grandChildren.append(os.path.join(x[0], grandKid))
或者,可以用列表推导式来代替循环:
import os
root='.'
grandChildren = [
os.path.join(kid, grandKid)
for kid in next(os.walk(root))[1]
for grandKid in next(os.walk(os.path.join(root, kid)))[1]]
最后,把os.walk
提取到一个函数中:
def read_subdirs(dir='.'):
import os
return (os.path.join(dir,x) for x in next(os.walk(dir))[1])
root='.'
grandChildren = [
grandKid
for kid in read_subdirs(root)
for grandKid in read_subdirs(kid)]
通过测试,我们可以看到,如果有曾孙目录,我的方法调用
stat
的次数明显少于你的方法。
例如,在我的主目录中,我运行了我的代码(/tmp/a.py
)和你的代码(/tmp/b.py
),在每种情况下root
都设置为'.'
:
$ strace -e stat python /tmp/a.py 2>&1 > /dev/null | egrep -c stat
1245
$ strace -e stat python /tmp/b.py 2>&1 > /dev/null | egrep -c stat
36049
5
如果我理解你的问题没错的话。
你可以使用 glob 这个工具来获取文件或文件夹,只需要用一些通配符就可以了。例如,如果你想要获取 "/home/" 目录下的所有文件夹,可以这样做。
glob.glob('/home/*/*/')
如果你还想知道所有的文件,也可以这样做。
glob.glob('/home/*/*')