如何通过一次OS调用获取python中目录的所有孙子

How to get all grandchildren of a directory in python with only one OS call

我试图在python中获取某个目录的所有孙子。出于性能方面的考虑,我不想一直在循环中调用操作系统函数(它是一个网络文件系统)。这就是我目前的情况。有更简单的方法吗?

1
2
3
4
5
6
7
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的电话是不是很懒。我的意思是,我打电话后,整棵树都应该在记忆中,但我不确定。


如果我能正确回答你的问题。

您可以使用glob获取文件或目录,方法是提供通配符标记。例如,要在列表中获取"/home/"中的所有dir,可以这样做。

1
glob.glob('/home/*/*/')

或者尽你所能了解所有的文件

1
glob.glob('/home/*/*')


在POSIX或Windows中,不能在一次操作系统调用中获取所有数据。对于posix,每个目录至少有三个(opendirreaddirclose)和一个目录条目(stat)。


我相信接下来的操作系统调用会比你发布的少。是的,os.walk()调用是懒惰的;也就是说,从walk()返回时,整个树都不在内存中,而是在调用next()时零碎地读取。

因此,我的版本将只在一阶后代目录中读取,而stat将只读取直接子女和孙子。您的版本也将为所有的曾孙做这项工作,因为您的目录结构很深。

1
2
3
4
5
6
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循环:

1
2
3
4
5
6
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分解成一个函数:

1
2
3
4
5
6
7
8
9
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设置为'.'

1
2
3
4
$ 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