如何在Python中获取所有直接子目录

Ste*_*ble 134 python file

我正在尝试编写一个简单的Python脚本,它将index.tpl复制到所有子目录中的index.html(除了少数例外).

我试图获取子目录列表而陷入困境.

Ric*_*dle 217

import os
def get_immediate_subdirectories(a_dir):
    return [name for name in os.listdir(a_dir)
            if os.path.isdir(os.path.join(a_dir, name))]
Run Code Online (Sandbox Code Playgroud)


use*_*036 104

我对各种函数进行了一些速度测试,以返回所有当前子目录的完整路径

tl;dr: 始终使用scandir

list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]

奖励:scandir您也可以简单地使用f.name而不是f.path.

这(以及下面的所有其他函数)不会使用自然排序。这意味着结果将按如下方式排序:1, 10, 2。要获得自然排序 (1, 2, 10),请查看/sf/answers/3362121521/




结果scandir是:比walk快3 倍,比listdir(带过滤器)快 32 倍,比Pathliblistdir35倍,比快36 倍,比 快37 倍(!)glob

Scandir:           0.977
Walk:              3.011
Listdir (filter): 31.288
Pathlib:          34.075
Listdir:          35.501
Glob:             36.277
Run Code Online (Sandbox Code Playgroud)

使用 W7x64、Python 3.8.1 进行测试。包含 440 个子文件夹的文件夹。
如果您想知道是否listdir可以通过不执行 os.path.join() 两次来加快速度,是的,但是差异基本上不存在。

代码:

import os
import pathlib
import timeit
import glob

path = r"<example_path>"



def a():
    list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
    # print(len(list_subfolders_with_paths))


def b():
    list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
    # print(len(list_subfolders_with_paths))


def c():
    list_subfolders_with_paths = []
    for root, dirs, files in os.walk(path):
        for dir in dirs:
            list_subfolders_with_paths.append( os.path.join(root, dir) )
        break
    # print(len(list_subfolders_with_paths))


def d():
    list_subfolders_with_paths = glob.glob(path + '/*/')
    # print(len(list_subfolders_with_paths))


def e():
    list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
    # print(len(list(list_subfolders_with_paths)))


def f():
    p = pathlib.Path(path)
    list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
    # print(len(list_subfolders_with_paths))



print(f"Scandir:          {timeit.timeit(a, number=1000):.3f}")
print(f"Listdir:          {timeit.timeit(b, number=1000):.3f}")
print(f"Walk:             {timeit.timeit(c, number=1000):.3f}")
print(f"Glob:             {timeit.timeit(d, number=1000):.3f}")
print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
print(f"Pathlib:          {timeit.timeit(f, number=1000):.3f}")
Run Code Online (Sandbox Code Playgroud)


ari*_*ari 72

为什么没有人提到globglob让你使用Unix风格的路径名扩展,并且几乎所有需要找到多个路径名的东西都可以使用.这很容易:

from glob import glob
paths = glob('*/')
Run Code Online (Sandbox Code Playgroud)

请注意,glob将返回带有最终斜杠的目录(如unix所示),而大多数path基于解决方案的操作将省略最终斜杠.

  • 通过构造,你可以保证有一个斜杠(所以它不是更安全),但我认为它更具可读性.你肯定想使用`rstrip`而不是`strip`,因为后者会将任何完全限定的路径转换为相对路径. (7认同)
  • 作为python新手的@ari评论的补充,例如I:`strip('/')`将删除起始和尾随'/',`rstrip('/')`将仅删除尾随的 (7认同)
  • 简单地用`[p [: - 1]为路径中的p]切割最后一个字符可能更安全,因为替换方法也将替换文件名中的任何转义正斜杠(不是那些常见的). (4认同)
  • 好的解决方案,简单而有效.对于那些不想要最终斜线的人,他可以在glob('*/')]中使用`paths = [p.replace('/','')来表示p. (3认同)
  • 更安全,使用条带('/')删除尾部斜杠.这样可以保证不会删除任何不是正斜杠的字符 (3认同)

Gen*_*wen 31

选中" 获取当前目录中所有子目录的列表 ".

这是一个Python 3版本:

import os

dir_list = next(os.walk('.'))[1]

print(dir_list)
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这将返回不带有父目录名称的子目录名称。 (3认同)
  • **非常聪明.**虽然效率并不重要(_...它完全没做),但我很好奇这是否是基于全局的生成器表达式`(s.rstrip("/")for s在glob(parent_dir +"*/"))中``更有时间效率.我直觉上怀疑``stat()`'`os.walk()`解决方案*应该比shell风格的globbing快得多.可悲的是,我缺乏"timeit"的意志并且实际上已经找到了. (2认同)

Mil*_*lan 19

import os, os.path
Run Code Online (Sandbox Code Playgroud)

要在目录中获取(完整路径)直接子目录:

def SubDirPath (d):
    return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
Run Code Online (Sandbox Code Playgroud)

要获取最新(最新)子目录:

def LatestDirectory (d):
    return max(SubDirPath(d), key=os.path.getmtime)
Run Code Online (Sandbox Code Playgroud)


And*_*Cox 11

os.walk 在这种情况下是你的朋友.

直接来自文档:

walk()通过从上到下或从下到上走树来生成目录树中的文件名.对于在目录顶部根的树的每个目录(包括顶部本身),它产生一个3元组(dirpath,dirnames中,文件名).


Sua*_*ris 10

这种方法很好地一气呵成.

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
Run Code Online (Sandbox Code Playgroud)


Gly*_*yph 7

使用Twisted的FilePath模块:

from twisted.python.filepath import FilePath

def subdirs(pathObj):
    for subpath in pathObj.walk():
        if subpath.isdir():
            yield subpath

if __name__ == '__main__':
    for subdir in subdirs(FilePath(".")):
        print "Subdirectory:", subdir
Run Code Online (Sandbox Code Playgroud)

由于一些评论者已经问过使用Twisted库的优势是什么,我将在这里略微超出原来的问题.


在分支中有一些改进的文档解释了FilePath的优点; 你可能想读这个.

更具体地说,在此示例中:与标准库版本不同,此函数可以在没有导入的情况下实现."subdirs"函数是完全通用的,因为它只对其参数进行操作.为了使用标准库复制和移动文件,您需要依赖于" open"内置," listdir",或许" isdir"或" os.walk"或" shutil.copy".也许" os.path.join"也是.更不用说你需要一个传递参数的字符串来识别实际文件.让我们看看将每个目录的"index.tpl"复制到"index.html"的完整实现:

def copyTemplates(topdir):
    for subdir in subdirs(topdir):
        tpl = subdir.child("index.tpl")
        if tpl.exists():
            tpl.copyTo(subdir.child("index.html"))
Run Code Online (Sandbox Code Playgroud)

上面的"subdirs"函数可以在任何类似FilePath的对象上工作.除其他外,这意味着ZipPath对象.不幸的ZipPath是,它现在是只读的,但它可以扩展到支持写作.

您还可以传递自己的对象以进行测试.为了测试这里建议的os.path-using API,你必须使用导入的名称和隐式依赖项,并且通常执行黑魔法以使测试工作.使用FilePath,您可以执行以下操作:

class MyFakePath:
    def child(self, name):
        "Return an appropriate child object"

    def walk(self):
        "Return an iterable of MyFakePath objects"

    def exists(self):
        "Return true or false, as appropriate to the test"

    def isdir(self):
        "Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))
Run Code Online (Sandbox Code Playgroud)