我一直在尝试获取子目录列表而感到困扰。
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))]
我对返回所有当前子目录全路径的各种函数进行了一些速度测试.
总之,始终使用scandir
:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
额外加赠:使用scandir
您还可以仅通过使用f.name
而不是f.path
来获取文件夹名称。
这(以及下面的所有其他功能)都不使用natural sorting。这意味着结果将按如下方式排序:1、10、2。 要获得自然排序(1、2、10),请查看https://dev59.com/Gm445IYBdhLWcg3wia2V#48030307
结果:
scandir
比walk
快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个子文件夹的文件夹。
如果您想知道是否可以通过不进行两次os.path.join()来加速listdir
,答案是肯定的,但实际上差别几乎无法察觉。
代码:
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}")
为什么没有人提到 glob
?glob
允许您使用类 Unix 的路径名扩展,几乎可以用于查找多个路径名的所有操作。这使得它非常容易:
from glob import glob
paths = glob('*/')
注意,glob
会返回带有最终斜杠的目录(如 Unix),而大多数基于 path
的解决方案将省略最终斜杠。
paths = [ p.replace('/', '') for p in glob('*/') ]
。 - Evan Hu[p[:-1] for p in paths]
可能更安全,因为 replace 方法也会替换文件名中任何转义的正斜杠(虽然这种情况很少见)。 - arirstrip
而不是 strip
,因为后者会将任何完全限定的路径转换为相对路径。 - aristrip('/')
会同时移除开头和结尾的斜杠,而rstrip('/')
只会移除结尾的斜杠。 - Titou(s.rstrip("/") for s in glob(parent_dir+"*/"))
哪个更加高效。我的直觉是基于stat()
的os.walk()
解决方案应该比shell风格的globbing快得多。可惜,我没有意愿去使用timeit
来真正找出答案。 - Cecil Currytmplist=(s.rstrip("/") for s in glob(tmp+"*/"))
,其中tmp是我的父目录。它返回 <generator object <genexpr> at 0x0000018A88EE3660>
。我做错了什么? - jriveimport os
获取目录中所有(完整路径的)直接子目录:
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(...) )
。 - user136036os.walk
是在这种情况下的好帮手。
引用自官方文档:
walk() 方法通过向上或向下遍历目录树,在目录树中生成文件名。对于目录树中的每个目录(包括顶级目录 top 本身),它会产生一个 3 元组(dirpath,dirnames,filenames)。
这种方法很好地一次性完成所有工作。
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
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"))
FilePath
的对象。这意味着,ZipPath
对象等等也可以使用。不幸的是,ZipPath
目前是只读的,但它可以扩展以支持写入。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(...))
我刚编写了一些代码来移动VMware虚拟机,并最终使用os.path
和shutil
来实现子目录之间的文件复制。
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))
这并不是非常优雅,但它确实有效。
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(directory) -> 返回所有子文件夹列表