协慌网

登录 贡献 社区

如何在 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 (感谢 kanja,Blair 和 Douglas)。这就是我现在拥有的:

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

是否有 “开放” 的标志,这会自动发生?

答案

我看到两个具有良好品质的答案,每个答案都有一个小缺陷,所以我会考虑它:

尝试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 3.5+:

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

pathlib.Path.mkdir所使用的pathlib.Path.mkdir以递归方式创建目录,并且如果该目录已存在则不会引发异常。如果您不需要或不想创建parents ,请跳过parents参数。

Python 3.2+:

使用pathlib

如果可以,请安装名为pathlib2的当前pathlib backport。不要安装名为pathlib的旧的非维护pathlib 。接下来,请参阅上面的 Python 3.5 + 部分并使用它。

如果使用 Python 3.4,即使它带有pathlib ,它也缺少有用的exist_ok选项。 backport 旨在提供更新更好的mkdir实现,其中包括这个缺少的选项。

使用os

import os
os.makedirs(path, exist_ok=True)

os.makedirs使用的os.makedirs以递归方式创建目录,如果该目录已存在则不会引发异常。它只有在使用 Python 3.2 + 时才有可选的exist_ok参数,默认值为False 。这个参数在 Python 2.x 中不存在,最高可达 2.7。因此,不需要像 Python 2.7 那样进行手动异常处理。

Python 2.7+:

使用pathlib

如果可以,请安装名为pathlib2的当前pathlib backport。不要安装名为pathlib的旧的非维护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创建嵌套目录,如果该目录已存在mkpath执行任何操作。这适用于 Python 2 和 3。

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

根据Bug 10948 ,这种替代方案的一个严重限制是,对于给定路径,每个 python 进程只能运行一次。换句话说,如果你用它来创建一个目录,然后从 Python 内部或外部删除目录,然后再次使用mkpath重新创建相同的目录, mkpath将默默地使用其先前创建目录的无效缓存信息,并且实际上不会再次创建目录。相比之下, os.makedirs并不依赖于任何此类缓存。对于某些应用,此限制可能没问题。


关于目录的模式 ,如果您关心它,请参阅文档。

使用 try 除了和 errno 模块的正确错误代码摆脱了竞争条件并且是跨平台的:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

换句话说,我们尝试创建目录,但如果它们已经存在,我们会忽略错误。另一方面,报告任何其他错误。例如,如果您事先创建errno.EACCES ' 并从中删除所有权限,则会出现使用errno.EACCES引发的OSError (权限被拒绝,错误 13)。