如何在Python中使用一次OS调用获取目录的所有子孙节点

3

我正在尝试在Python中获取特定目录的所有子代。出于性能原因,我不希望在循环中继续调用OS函数(它是一个网络文件系统)。以下是我目前拥有的内容,是否有更简单的方法来实现这个功能?

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调用是否是惰性的。我的意图是在调用后整个目录树应该在内存中,但我不确定。


你看过 unipath 模块了吗? - Henrik Andersson
这个答案可能会有所启发:https://dev59.com/Xm855IYBdhLWcg3w1oE8#4117594。 - Robᵩ
你只对孙子有兴趣还是对所有第二代和更高一级后代都感兴趣?也就是说,'root/a/b/c/d'会被包括在搜索范围内还是被排除在外? - Robᵩ
2个回答

5
如果我理解你的问题正确。
您可以使用glob通过给定通配符来获取文件或目录。例如,要在列表中获取“/home/”内所有目录,可以执行以下操作。
glob.glob('/home/*/*/')

或者你想要了解所有文件,也可以这样做。
glob.glob('/home/*/*')

这并不是很有用,因为您需要知道每个目录中子目录的数量。 - msvalkon
jjujuma: grandChildren = [dirpath.rstrip(os.sep) for dirpath in glob.iglob('/home/*/*/')] 生成了与您的代码相同的列表。rstrip() 会从列表中的目录路径中删除尾部的路径分隔符。@msvalkon:我认为你错了。 - martineau
@martineau 噢,是的,我理解grandChildren/foo/bar的所有子目录。 - msvalkon

1
在 POSIX 和 Windows 中,你不能通过一个操作系统调用获取所有这些数据。至少对于 POSIX,每个目录需要三个调用(opendirreaddirclose),再加上每个目录条目需要一个调用(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))

或者,可以使用列表推导式代替for循环:

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

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