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

我试图写一个简单的Python脚本,将复制索引。在所有子目录(少数例外)中将TPL转换为index.html。

我在获取子目录列表时陷入了困境。

216800 次浏览
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))]

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

直接从文档中:

Walk()通过自顶向下或自底向上遍历目录树来生成目录树中的文件名。对于根在目录顶部的树中的每个目录(包括top本身),它会生成一个3元组(dirpath、dirnames、filename)。

使用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。更不用说需要传递参数的字符串来标识实际的文件。让我们看一下完整的实现,它将复制每个目录的“索引”。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"函数可以作用于任何类似__abc0的对象。这意味着,除其他外,ZipPath对象。不幸的是,ZipPath目前是只读的,但可以扩展为支持写入。

您还可以为测试目的传递自己的对象。为了测试操作系统。在这里建议使用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(...))

这里有一种方法:

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')

我只是写了一些代码来移动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))

它不是特别优雅,但确实有用。

import os

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

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

获取最新(latest)子目录:

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

为什么没有人提到glob?glob允许您使用unix风格的路径名展开,并且是我的go to函数,几乎所有需要查找多个路径名的东西都可以使用它。这很简单:

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

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

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

下面是Python 3的版本:

import os


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


print(dir_list)

这个方法很好地一次性完成了这一切。

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
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_directores_recurrecursive (directory, index=1) ->给出第一层的文件夹列表

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

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']

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

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

my_dir.dirs()

完整的工作示例是:

from path import Path


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


subdirs = my_directory.dirs()

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

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')

我在各种函数上做了一些速度测试,将完整路径返回到所有当前子目录。

< p > tl;博士: 总是使用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

< br >


< br >

< p > 结果: scandirwalk快3倍,比listdir快32倍(带过滤器),比Pathlib快35倍,比listdir快36倍,比glob快37倍(!)
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}")

使用pathlib的一行代码:

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]