一尘不染

Pyrhon-如何安全地创建嵌套目录?

python

检查文件目录是否存在的最优雅方法是什么(如果不存在),使用Python创建目录?这是我尝试过的:

import os

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(感谢魔芋,布莱尔和道格拉斯)。这就是我现在所拥有的:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

是否有“打开”标志,使它自动发生?


阅读 448

收藏
2020-02-12

共2个答案

一尘不染

在Python≥3.5上,使用pathlib.Path.mkdir

from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)

对于较旧的Python版本,我看到两个质量不错的答案,每个答案都有一个小缺陷,因此我将对此进行说明:

试试看os.path.exists,然后考虑os.makedirs创建。

import os
if not os.path.exists(directory):
    os.makedirs(directory)

如注释和其他地方所述,这是一个竞争条件–如果在os.path.existsos.makedirs调用之间创建目录,os.makedirs则将失败并显示OSError。不幸的是,毯式捕获OSError和继续操作并非万无一失,因为它会忽略由于其他因素(例如权限不足,磁盘已满等)而导致的目录创建失败。

一种选择是捕获OSError并检查嵌入式错误代码(请参阅是否存在从Python的OSError获取信息的跨平台方法):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

另外,可能还有第二个os.path.exists,但是假设另一个在第一次检查后创建了目录,然后在第二次检查之前将其删除了–我们仍然可能会被愚弄。

取决于应用程序,并发操作的危险可能比其他因素(例如文件许可权)造成的危险更大或更小。在选择实现之前,开发人员将必须了解有关正在开发的特定应用程序及其预期环境的更多信息。

现代版本的Python通过暴露FileExistsError(在3.3+ 版本中)都极大地改善了此代码。

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

…并通过允许关键字参数os.makedirs调用exist_ok(在3.2+版本中)。

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
2020-02-12
一尘不染

Python 3.5以上版本:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir上面使用的递归方式创建目录,并且如果目录已经存在,则不会引发异常。如果不需要或不希望创建父母,请跳过该parents参数。

Python 3.2以上版本:

使用pathlib

如果可以,请安装pathlib名为的当前反向端口pathlib2。不要安装名为的较旧的未维护的反向端口pathlib。接下来,请参考上面的Python 3.5+部分,并对其进行相同的使用。

如果使用Python 3.4,即使附带了pathlib,它也会丢失有用的exist_ok选项。反向端口旨在提供更新的高级实现,mkdir其中包括缺少的选项。

使用os:

import os
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

如果可以,请安装pathlib名为的当前反向端口pathlib2。不要安装名为的较旧的未维护的反向端口pathlib。接下来,请参考上面的Python 3.5+部分,并对其进行相同的使用。

使用os:

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

虽然可能会先使用朴素的解决方案,os.path.isdir然后再使用os.makedirs,但是上述解决方案颠倒了两个操作的顺序。这样,它可以防止由于创建目录的重复尝试而导致的常见竞争状况,并且还可以消除目录中文件的歧义。

请注意,捕获异常和使用errno的作用有限,因为对于文件和目录,都会引发OSError: [Errno 17] File exists,即errno.EEXIST。仅检查目录是否存在更为可靠。

选择:

mkpath创建嵌套目录,如果目录已经存在,则不执行任何操作。这适用于Python 2和3。

import distutils.dir_util
distutils.dir_util.mkpath(path)

根据Bug 10948,此替代方案的严重局限性在于,对于给定路径,每个python进程仅工作一次。换句话说,如果你使用它来创建目录,然后从Python内部或外部删除该目录,然后mkpath再次mkpath使用它来重新创建相同的目录,则将简单地静默使用其先前已创建目录的无效缓存信息,而不会实际重新创建目录。相反,os.makedirs不依赖任何此类缓存。对于某些应用程序,此限制可能还可以。

2020-02-12