检查文件将要写入的目录是否存在最优雅的方法是什么,如果不是,使用Python创建目录?这是我试过的:
importos
file_path= "/my/directory/filename.txt"directory=os.path.dirname(file_path)
try:os.stat(directory)
except:os.mkdir(directory)f=file(filename)
不知何故,我错过了os.path.exists(感谢kanja,布莱尔和道格拉斯)。这是我现在拥有的:
defensure_dir(file_path):directory=os.path.dirname(file_path)
if notos.path.exists(directory):os.makedirs(directory)
有没有“开放”的标志,这会自动发生?
答案
我看到两个质量很好的答案,每个答案都有一个小缺陷,所以我会考虑一下:
importosif notos.path.exists(directory):os.makedirs(directory)
正如评论和其他地方所指出的那样,竞争条件 – 如果目录是在创建os.path.exists和os.makedirs调用之间创建的,os.makedirs则会失败并显示一个OSError。不幸的是,一揽子捕捉OSError和继续并不是万无一失的,因为它会忽略由于其他因素造成的目录失败,如权限不足,磁盘满等。
一种选择是捕获OSError和检查嵌入的错误代码(请参阅从Python的OSError获取信息是否有跨平台的方式):
importos,errnotry:os.makedirs(directory)
except OSError ase:
ife.errno!=errno.EEXIST:
raise
或者,可能会有第二个os.path.exists,但假设另一个在第一次检查后创建目录,然后在第二个检查之前将其删除 – 我们仍然可能被愚弄。
根据应用程序的不同,并发操作的危险可能比文件许可等其他因素带来的危险更大或更小。在选择实现之前,开发人员必须更多地了解正在开发的特定应用程序及其预期环境。
Python 3.5+:
importpathlib
pathlib.Path('/my/directory').mkdir(parents=True,exist_ok=True)
pathlib.Path.mkdir如上面所使用的那样递归地创建目录,并且如果该目录已经存在则不会引发异常。如果您不需要或不想创建父母,请跳过该parents论点。
Python 3.2+:
使用pathlib:
如果可以,请安装指定的当前pathlibbackport pathlib2。不要安装旧的未维护的backport pathlib。接下来,请参阅上面的Python 3.5+部分并使用它。
如果使用Python 3.4,即使它附带了pathlib,backport也是为了提供更新,更高级的实现mkdir。
使用os:
importos
os.makedirs(path,exist_ok=True)
os.makedirs如上面所使用的那样递归地创建目录,并且如果该目录已经存在则不会引发异常。它exist_ok仅在使用Python 3.2+时具有可选参数,默认值为False。Python 2.x中不存在此参数,最高为2.7。因此,与Python 2.7一样,不需要手动异常处理。
Python 2.7+:
使用pathlib:
如果可以,请安装指定的当前pathlibbackport pathlib2。不要安装旧的未维护的backport pathlib。接下来,请参阅上面的Python 3.5+部分并使用它。
使用os:
importostry:os.makedirs(path)
except OSError:
if notos.path.isdir(path):
raise
而幼稚溶液可首先使用os.path.isdir,接着os.makedirs,在溶液上方反转两个操作的顺序。这样做可以防止共同的竞争条件与创建目录时的重复尝试有关,也可以消除目录中的文件歧义。
请注意,捕获异常并使用errno的用处有限,因为OSError: [Errno 17] File exists即errno.EEXIST针对文件和目录引发异常。简单地检查目录是否存在更为可靠。
替代方案:
mkpath创建嵌套目录,如果该目录已经存在,则不执行任何操作。这适用于Python 2和Python 3。
importdistutils.dir_util
distutils.dir_util.mkpath(path)
根据Bug 10948,这种替代方案的一个严重限制是它对于给定的路径在每个Python进程中仅工作一次。换句话说,如果你使用它来创建一个目录,那么从Python内部或外部删除目录,然后mkpath再次使用来重新创建相同的目录,mkpath将简单地静静地使用其以前创建目录的无效缓存信息,而不会实际上再次制作目录。相比之下,os.makedirs不依赖于任何这样的缓存。对于某些应用程序,此限制可能没有问题。
使用除errno模块之外的尝试以及正确的错误代码摆脱竞争条件并且是跨平台的:
importosimporterrnodefmake_sure_path_exists(path):
try:os.makedirs(path)
except OSError asexception:
ifexception.errno!=errno.EEXIST:
raise
换句话说,我们尝试创建目录,但是如果它们已经存在,我们会忽略该错误。另一方面,报告任何其他错误。例如,如果您事先创建dir’a’并从中删除所有权限,您将得到一个OSError带有errno.EACCES(Permission denied,错误13)的引发。
检查os.makedirs :(它确保存在完整路径。)
为了处理目录可能存在的事实,请捕获OSError。(如果exist_ok为False(默认值),如果目标目录已经存在,则会引发OSError。)
importostry:os.makedirs('./path/to/somewhere')
except OSError:
pass
洞察这种情况的具体情况
你在特定的路径下给出一个特定的文件,然后从文件路径中取出目录。然后,确保您有目录后,您尝试打开一个文件进行阅读。对此代码发表评论:
filename= "/my/directory/filename.txt"dir=os.path.dirname(filename)
我们想避免覆盖内置函数,dir。另外,filepath或者也许可能fullfilepath是一个更好的语义名称比filename这样写得更好:
importos
filepath= '/my/directory/filename.txt'directory=os.path.dirname(filepath)
你的最终目标是打开这个文件,你最初说,为了写作,但你基本上正在接近这个目标(基于你的代码),就像这样,它打开阅读文件:
if notos.path.exists(directory):os.makedirs(directory)f=file(filename)
假设开放阅读
你为什么要为你希望在那里并能够阅读的文件创建一个目录?
试图打开文件。
withopen(filepath) asmy_file:do_stuff(my_file)
如果目录或文件不存在,您将得到一个IOError关联的错误编号:errno.ENOENT将指向正确的错误编号,而不管您的平台。如果你愿意,你可以抓住它,例如:
importerrnotry:
withopen(filepath) asmy_file:do_stuff(my_file)
except IOError aserror:
iferror.errno==errno.ENOENT:
print 'ignoring error because directory or file is not there'
else:
raise
假设我们正在写作
这可能是你想要的。
在这种情况下,我们可能没有面临任何竞争条件。所以,就像你一样,但要注意写作时,你需要用w模式打开(或a追加)。这也是使用上下文管理器打开文件的Python最佳实践。
importosif notos.path.exists(directory):os.makedirs(directory)
withopen(filepath, 'w') asmy_file:do_stuff(my_file)
但是,假设我们有几个Python进程试图将他们所有的数据放到同一个目录中。那么我们可能会争夺目录的创建。在这种情况下,最好将makedirs调用包装在try-except块中。
importosimporterrnoif notos.path.exists(directory):
try:os.makedirs(directory)
except OSError aserror:
iferror.errno!=errno.EEXIST:
raise
withopen(filepath, 'w') asmy_file:do_stuff(my_file)