Answers:
我对各种功能进行了一些速度测试,以将完整路径返回到所有当前子目录。
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倍Pathlib
快listdir
35 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}")
为什么没有人提到glob
?glob
使您可以使用Unix样式的路径名扩展,并且对于需要查找多个路径名的几乎所有内容,我都可以使用。它非常容易:
from glob import glob
paths = glob('*/')
请注意,这glob
将返回带有最后斜杠的目录(与Unix一样),而大多数path
基于解决方案的目录将省略最后斜杠。
paths = [ p.replace('/', '') for p in glob('*/') ]
。
[p[:-1] for p in paths]
,因为该replace方法还将替换文件名中所有转义的正斜杠(不是常见的斜杠)。
rstrip
代替strip
,因为后者会将所有完全限定的路径转换为相对路径。
strip('/')
将删除开头和结尾的“ /”,rstrip('/')
仅删除结尾的“ /”
(s.rstrip("/") for s in glob(parent_dir+"*/"))
是否更省时感到好奇。我的直觉怀疑是,stat()
基于os.walk()
解决方案的解决方案应比外壳样式的globping快得多。可悲的是,我缺乏去timeit
寻找现实的意志。
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(...) )
。
os.walk
在这种情况下是你的朋友。
直接来自文档:
walk()通过自上而下或自下而上遍历树来在目录树中生成文件名。对于以目录顶部(包括顶部本身)为根的树中的每个目录,它会生成一个三元组(目录路径,目录名,文件名)。
使用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(...))
这是一种方法:
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')
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(目录)->给出所有子文件夹
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']
一种使用pathlib的衬板:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]