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


Answers:


31

我对各种功能进行了一些速度测试,以将完整路径返回到所有当前子目录。

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),请查看https://stackoverflow.com/a/48030307/2441026




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

Scandir:           0.977
Walk:              3.011
Listdir (filter): 31.288
Pathlib:          34.075
Listdir:          35.501
Glob:             36.277

已在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}")

1
只想谢谢你,真的是在寻找这个。很好的分析。
Cing

225
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))]

76

为什么没有人提到globglob使您可以使用Unix样式的路径名扩展,并且对于需要查找多个路径名的几乎所有内容,我都可以使用。它非常容易:

from glob import glob
paths = glob('*/')

请注意,这glob将返回带有最后斜杠的目录(与Unix一样),而大多数path基于解决方案的目录将省略最后斜杠。


3
好的解决方案,简单而有效。对于那些不希望使用最后斜线的人,他可以使用this paths = [ p.replace('/', '') for p in glob('*/') ]
Evan Hu

5
用剪切最后一个字符可能更安全[p[:-1] for p in paths],因为该replace方法还将替换文件名中所有转义的正斜杠(不是常见的斜杠)。
ARI

3
更安全的是,使用strip('/')删除斜杠。这样可以确保您不会剪切掉任何非正斜杠的字符
Eliezer Miron

8
通过构造,可以确保您在末尾使用斜杠(因此不安全),但是我确实认为它更具可读性。但是,您肯定要使用rstrip代替strip,因为后者会将所有完全限定的路径转换为相对路径。
2016年

7
python新手的@ari注释的补充,例如I:strip('/')将删除开头和结尾的“ /”,rstrip('/')仅删除结尾的“ /”
Titou

35

选中“ 获取当前目录中所有子目录的列表 ”。

这是Python 3版本:

import os

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

print(dir_list)

2
非常聪明。尽管效率并不重要(...完全无关紧要),但我对此或基于glob的生成器表达式(s.rstrip("/") for s in glob(parent_dir+"*/"))是否更省时感到好奇。我的直觉怀疑是,stat()基于os.walk()解决方案的解决方案比外壳样式的globping快得多。可悲的是,我缺乏去timeit寻找现实的意志。
塞西尔·库里

3
请注意,这将返回不带有父目录名称的子目录名称。
Paul Chernoch '17

19
import os, os.path

要获取目录中的(全路径)直接子目录:

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

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

def LatestDirectory (d):
    return max(SubDirPath(d), key=os.path.getmtime)

要获取列表,只需添加list( filter(...) )
user136036

12

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

直接来自文档:

walk()通过自上而下或自下而上遍历树来在目录树中生成文件名。对于以目录顶部(包括顶部本身)为根的树中的每个目录,它会生成一个三元组(目录路径,目录名,文件名)。


1
请注意,如果只需要第一级子目录,则在第一组返回值之后中断os.walk迭代。
yoyo 2015年

11

这种方法可以很好地一劳永逸。

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]

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

由于一些评论者曾问过使用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"))

上面的“ subdirs”功能可以在任何类似FilePath对象上使用。除其他外,这意味着ZipPath对象。不幸的ZipPath是,目前它是只读的,但是可以扩展为支持写作。

您还可以传递自己的对象以进行测试。为了测试此处建议的使用os.path的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(...))

由于我很少接触Twisted,因此我一直欢迎其他信息和示例;这个答案很高兴看到。话虽如此,由于与使用内置的python模块和Twisted安装相比,此方法似乎需要更多的工作,因此使用此方法是否有任何好处,您可以将其添加到答案中?
Jarret Hardie

1
Glyph的答案很可能是受TwistedLore也使用.tpl文件的事实启发的。
康斯坦丁

好吧,很明显,我不希望在西班牙进行审理:-)我以为“ * .tpl”是对某些抽象扩展的通用引用,意思是“模板”,而不是特定的Twisted模板(我已经在很多情况下看到过.tpl语言)。很高兴知道。
Jarret Hardie

因此,+ 1是为了限制可能的Twisted角度,尽管我仍然想了解Twisted的'FilePath'对象和'walk()'函数在标准API中添加了什么。
Jarret Hardie

我个人发现“ FilePath.walk()产生路径对象”比“ os.walk产生3个元组的dir,dirs,files”要容易记住。但是还有其他好处。FilePath允许多态,这意味着您可以遍历文件系统以外的内容。例如,您可以将twisted.python.zippath.ZipArchive传递给我的'subdirs'函数,并获得ZipPaths的生成器而不是FilePaths;您的逻辑不会改变,但是您的应用程序现在可以神奇地处理zip文件。如果要测试它,则只需提供一个对象,而不必编写实际文件。
雕文

4

我只是编写了一些代码来移动vmware虚拟机,最终使用os.pathshutil完成了子目录之间的文件复制。

def copy_client_files (file_src, file_dst):
    for file in os.listdir(file_src):
            print "Copying file: %s" % file
            shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))

它并不十分优雅,但确实可以工作。


1

这是一种方法:

import os
import shutil

def copy_over(path, from_name, to_name):
  for path, dirname, fnames in os.walk(path):
    for fname in fnames:
      if fname == from_name:
        shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))


copy_over('.', 'index.tpl', 'index.html')

-1:将不起作用,因为shutil.copy将复制到当前目录,因此对于在子目录树中找到的每个“ index.tpl”,您最终都会覆盖一次当前目录中的“ index.html”。
nosklo

1

我不得不提到path.py库,该库我经常使用。

提取直接子目录变得如此简单:

my_dir.dirs()

完整的工作示例是:

from path import Path

my_directory = Path("path/to/my/directory")

subdirs = my_directory.dirs()

注意:my_directory仍然可以作为字符串操作,因为Path是字符串的子类,但是提供了许多有用的方法来处理路径


1
def get_folders_in_directories_recursively(directory, index=0):
    folder_list = list()
    parent_directory = directory

    for path, subdirs, _ in os.walk(directory):
        if not index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)
        elif path[len(parent_directory):].count('/') + 1 == index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)

    return folder_list

以下函数可以称为:

get_folders_in_directories_recursively(directory,index = 1)->给出第一级的文件夹列表

get_folders_in_directories_recursively(目录)->给出所有子文件夹


做得很好,版本python 3.6,但是我需要从函数变量内部擦除“ self”
locometro

1
在班级内部使用,已更新
Kanish Mathew

0
import glob
import os

def child_dirs(path):
     cd = os.getcwd()        # save the current working directory
     os.chdir(path)          # change directory 
     dirs = glob.glob("*/")  # get all the subdirectories
     os.chdir(cd)            # change directory to the script original location
     return dirs

child_dirs函数采用目录路径,并返回其中的直接子目录列表。

dir
 |
  -- dir_1
  -- dir_2

child_dirs('dir') -> ['dir_1', 'dir_2']

0
import pathlib


def list_dir(dir):
    path = pathlib.Path(dir)
    dir = []
    try:
        for item in path.iterdir():
            if item.is_dir():
                dir.append(item)
        return dir
    except FileNotFoundError:
        print('Invalid directory')

0

一种使用pathlib的衬板:

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.