3

我正在尝试在 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 的调用是否是懒惰的。我的意图是在我打电话后整棵树都应该在内存中,但我不确定。

4

2 回答 2

5

如果我的问题是正确的。

您可以使用 glob 通过提供通配符符号来获取文件或目录。例如,在列表中获取“/home/”中的所有目录,您可以这样做。

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

或者要知道所有文件,你可以做

glob.glob('/home/*/*')
于 2013-03-06T20:17:42.933 回答
1

在 POSIX 或 Windows 中,您无法在一次操作系统调用中获取所有数据。opendir对于 POSIX,每个目录( , 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))

或者,作为列表推导而不是 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.walks 分解为一个函数:

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
于 2013-03-06T20:13:18.870 回答