【发布时间】:2010-10-22 11:03:33
【问题描述】:
我正在尝试编写一个简单的 Python 脚本,它将 index.tpl 复制到所有子目录中的 index.html 中(有一些例外)。
试图获取子目录列表让我陷入困境。
【问题讨论】:
-
您可能会发现在这个较早的 SO 问题中接受的答案解决了问题:stackoverflow.com/questions/120656/directory-listing-in-python
我正在尝试编写一个简单的 Python 脚本,它将 index.tpl 复制到所有子目录中的 index.html 中(有一些例外)。
试图获取子目录列表让我陷入困境。
【问题讨论】:
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))]
【讨论】:
我对各种函数进行了一些速度测试,以返回所有当前子目录的完整路径。
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
结果:
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 个子文件夹的文件夹。
如果您想知道 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] 简单地剪切最后一个字符可能更安全,因为该替换方法还将替换文件名中任何转义的正斜杠(不是那些常见的)。
rstrip 而不是strip,因为后者会将任何完全限定的路径转换为相对路径。
strip('/') 将删除起始和尾随 '/',rstrip('/') 将仅删除尾随
勾选“Getting a list of all subdirectories in the current directory”。
这是 Python 3 版本:
import os
dir_list = next(os.walk('.'))[1]
print(dir_list)
【讨论】:
(s.rstrip("/") for s in glob(parent_dir+"*/")) 更省时。我的直觉怀疑是基于stat() 的os.walk() 解决方案应该比shell 风格的通配要快得多。可悲的是,我缺乏timeit 的意愿并真正找出答案。
tmplist=(s.rstrip("/") for s in glob(tmp+"*/")),其中 tmp 是我的父目录。它返回<generator object <genexpr> at 0x0000018A88EE3660>。我做错了什么?
import 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(...) )。
os.walk 是你在这种情况下的朋友。
直接来自文档:
walk() 通过自上而下或自下而上遍历树来在目录树中生成文件名。对于以目录 top 为根的树中的每个目录(包括 top 本身),它会产生一个 3 元组(dirpath、dirnames、filenames)。
【讨论】:
这个方法很好地一次性完成。
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
【讨论】:
使用 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 的库有什么好处,我将在这里超越原来的问题。
在一个分支中有some improved documentation,它解释了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-like 对象。这意味着,除其他外,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(...))
【讨论】:
我刚刚写了一些代码来移动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))
它不是非常优雅,但确实有效。
【讨论】:
这是一种方法:
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')
【讨论】:
不得不提path.py 库,我经常使用它。
获取直接子目录就这么简单:
my_dir.dirs()
完整的工作示例是:
from path import Path
my_directory = Path("path/to/my/directory")
subdirs = my_directory.dirs()
注意:my_directory 仍然可以作为字符串进行操作,因为 Path 是字符串的子类,但提供了许多有用的方法来操作路径
【讨论】:
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) -> 给出所有子文件夹
【讨论】:
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']
【讨论】:
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')
【讨论】:
一个使用 pathlib 的班轮:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
【讨论】: