在问题How do I "cd" in python中,接受的答案建议在类中包装os.chdir调用以使返回原始目录异常安全。以下是推荐的代码:
class Chdir:
def __init__( self, newPath ):
self.savedPath = os.getcwd()
os.chdir(newPath)
def __del__( self ):
os.chdir( self.savedPath )
有人可以详细说明这是如何使不安全的呼叫异常安全的吗?
答案 0 :(得分:7)
线程安全和异常安全根本不是一回事。将os.chdir
调用包含在这样的类中是为了使其异常安全非线程安全。
异常安全是您经常听到的C ++开发人员所谈论的内容。在Python社区中几乎没有谈到它。来自Boost的Exception-Safety in Generic Components文档:
非正式地,例外安全 组件意味着它展示 异常时的合理行为 在执行期间抛出。对于 大多数人,“合理”一词 包括所有通常的期望 用于错误处理:该资源 不应该泄露,那个 程序应保留在 定义良好的状态,以便执行 可以继续。
因此,您提供的代码段中的想法是确保在异常的情况下,程序将返回到定义良好的状态。在这种情况下,进程将在它开始的目录中返回,无论os.chdir本身是否失败,或者是什么导致抛出异常并删除“Chdir”实例。
这种使用仅用于清理的对象的模式是“Resource Acquisition Is Initialization”或“RAII”的形式。这种技术在C ++中非常流行,但在Python中并不那么受欢迎,原因如下:
try
... finally
,它的用途几乎相同,是Python中比较常见的习惯用法。__del__
)在某些实现中不可靠/不可预测,因此以某种方式使用它们有点不鼓励。在cpython中,只要不涉及循环(即:当通过引用计数处理删除时),它们恰好是非常可靠和可预测的,但在其他实现中(Jython和我相信也是IronPython)当垃圾收集器到达时会发生删除它,可能会更晚。 (有趣的是,这并不能阻止大多数Python程序员依赖__del__
来关闭他们打开的文件。)编写上述代码的更“pythonic”方式是:
saved_path = os.getcwd()
os.chdir(new_path)
try:
# code that does stuff in new_path goes here
finally:
os.chdir(saved_path)
答案 1 :(得分:5)
这个问题的直接答案是:它没有,发布的代码很糟糕。
以下内容可能是合理的,使其“异常安全”(但更好的是避免使用chdir并使用完整路径):
saved_path = os.getcwd()
try:
os.chdir(newPath)
do_work()
finally:
os.chdir(saved_path)
这种精确的行为也可以写入上下文管理器。
答案 2 :(得分:1)
__del__
。因此,当您实例化此类时,当前工作目录将保存到实例属性,然后调用os.chdir。当实例被销毁时(无论出于何种原因),当前目录将更改为旧值。
这对我来说有点不对劲。据我所知,您必须在您的覆盖__del__
中呼叫父__del__
,所以应该更像这样:
class Chdir(object):
def __init__(self, new_path):
self.saved_path = os.getcwd()
os.chdir(new_path)
def __del__(self):
os.chdir(self.saved_path)
super(Chdir, self).__del__()
也就是说,除非我遗漏了某些东西。
(顺便说一句,你不能使用contextmanager做同样的事情吗?)
答案 3 :(得分:-2)
仅此代码既不是线程安全的,也不是异常安全的。实际上我并不确定你的例外是什么意思。以下代码浮现在脑海:
try:
# something thrilling
except:
pass
这是一个糟糕的主意。例外不是为了防范。编写良好的代码应该catch exceptions并对它们做一些有用的事情。