获取当前目录下所有子目录的列表

960 投票
35 回答
1544848 浏览
提问于 2025-04-15 12:08

有没有办法在Python中返回当前目录下所有子目录的列表呢?

我知道可以获取文件的列表,但我需要的是目录的列表。

35 个回答

323

这个方法比上面的要好很多,因为你不需要写很多次 os.path.join(),而且如果你想的话,可以直接得到完整的路径。这是在 Python 3.5 及以上版本中可以使用的。

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

这个方法会给你子目录的完整路径。如果你只想要子目录的名字,可以用 f.name 代替 f.path

https://docs.python.org/3/library/os.html#os.scandir


稍微偏题一下:如果你需要所有子文件夹(递归)和/或所有文件(递归),可以看看这个函数,它比 os.walkglob 更快,并且会返回所有子文件夹以及那些(子)子文件夹里的所有文件:https://stackoverflow.com/a/59803793/2441026

如果你只想要所有子文件夹(递归)

def fast_scandir(dirname):
    subfolders= [f.path for f in os.scandir(dirname) if f.is_dir()]
    for dirname in list(subfolders):
        subfolders.extend(fast_scandir(dirname))
    return subfolders

这个方法会返回所有子文件夹的完整路径列表。这个方法比 os.walk 快很多,比 glob 也快很多。


对所有函数的分析

总结一下:
- 如果你想获取一个文件夹的所有直接子目录,使用 os.scandir
- 如果你想获取所有子目录,包括嵌套的子目录,使用 os.walk 或者稍微快一点的 fast_scandir 函数。
- 不要只用 os.walk 来获取顶层子目录,因为它的速度可能比 os.scandir 慢上百倍!

  • 如果你运行下面的代码,确保先运行一次,这样你的操作系统才能访问到这个文件夹,丢弃结果后再进行测试,否则结果会不准确。
  • 你可能想要混合调用这些函数,但我测试过,这样做其实没什么影响。
  • 所有示例都会给出文件夹的完整路径。pathlib 示例会返回一个 (Windows)Path 对象。
  • os.walk 的第一个元素是基础文件夹,所以你不仅会得到子目录。你可以用 fu.pop(0) 来移除它。
  • 所有结果都不会使用自然排序。这意味着结果会像这样排序:1, 10, 2。要获取自然排序(1, 2, 10),请查看 https://stackoverflow.com/a/48030307/2441026


结果

os.scandir      took   1 ms. Found dirs: 439
os.walk         took 463 ms. Found dirs: 441 -> it found the nested one + base folder.
glob.glob       took  20 ms. Found dirs: 439
pathlib.iterdir took  18 ms. Found dirs: 439
os.listdir      took  18 ms. Found dirs: 439

在 W7x64 和 Python 3.8.1 下测试。

# -*- coding: utf-8 -*-
# Python 3


import time
import os
from glob import glob
from pathlib import Path


directory = r"<insert_folder>"
RUNS = 1


def run_os_walk():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [x[0] for x in os.walk(directory)]
    print(f"os.walk\t\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")


def run_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = glob(directory + "/*/")
    print(f"glob.glob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")


def run_pathlib_iterdir():
    a = time.time_ns()
    for i in range(RUNS):
        dirname = Path(directory)
        fu = [f for f in dirname.iterdir() if f.is_dir()]
    print(f"pathlib.iterdir\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")


def run_os_listdir():
    a = time.time_ns()
    for i in range(RUNS):
        dirname = Path(directory)
        fu = [os.path.join(directory, o) for o in os.listdir(directory) if os.path.isdir(os.path.join(directory, o))]
    print(f"os.listdir\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")


def run_os_scandir():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [f.path for f in os.scandir(directory) if f.is_dir()]
    print(f"os.scandir\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms.\tFound dirs: {len(fu)}")


if __name__ == '__main__':
    run_os_scandir()
    run_os_walk()
    run_glob()
    run_pathlib_iterdir()
    run_os_listdir()
327

你可以直接使用 glob.glob 这个方法。

from glob import glob
glob("/path/to/directory/*/", recursive = True)

别忘了在 * 后面加一个斜杠 /

936

你是问直接的子目录,还是树形结构下的所有目录呢?

无论哪种情况,你都可以使用 os.walk 来实现这个功能:

os.walk(directory)

这个方法会为每个子目录返回一个元组。这个三元组的第一个元素是目录的名字,所以

[x[0] for x in os.walk(directory)]

应该能给你所有的子目录,递归地获取。

需要注意的是,元组中的第二个元素是第一个位置的目录的子目录列表,所以你也可以用这个来获取,但这样做可能不会节省太多时间。

不过,你可以用它来获取直接的子目录:

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

或者你也可以查看已经发布的其他解决方案,使用 os.listdiros.path.isdir,包括那些在 "如何获取Python中的所有直接子目录" 的帖子中提到的。

撰写回答