资源获取即初始化,在Python

Resource Aquisition Is Initialization, in Python

我是 Python 的新手。我来自C++。

在一些代码审查中,有几个同事希望我将 initdel 中的内容移动到开始和停止方法。大多数时候,这与用几十年的 C++ 灌输到我脑海中的 RAII 背道而驰。

https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

RAII 不是 Python 中的东西吗? 不应该吗?

毕竟,我们可以抛出异常并且我们希望在抛出异常时释放资源,不是吗?

如果不是。有人可以深入了解为什么事情会有所不同吗?有没有我不懂的语言功能?

如果我有:

class Poop:
    def __init__:
        # Get some Windows Resource
    def __del__:
        #Release some Windows Resource

def foo():
    poop = Poop()
    raise Exception("Poop happens")

Windows资源释放了,对吧?

RAII 在 C++ 中工作,因为破坏是确定性的。

在像 Python 这样的垃圾收集语言中,您的对象可能 theoretically never be destroyed, even if you call del on it

无论如何,在 Python 中处理资源的惯用方式不是使用 RAII,也不是使用 start/stop,而是使用 上下文管理器 .

最简单的例子是文件对象:

with open('this_file.txt') as f:
    #  ... do stuff with f ...

# ... back to code that doesn't touch f ...

with 语句或多或少是一个 try-finally 块,它创建资源并确保在块结束时清理资源;像这样:

try:
    f = open('this_file.txt')
    #  ... do stuff with f ...

finally:
    f.close()

# ... back to code that doesn't touch f ...

我不知道Java,但我相信JVM也使用垃圾收集,同样try-finally是Java中资源管理的成语。

无论如何,with 语句采用 上下文管理器 ,它是定义 __enter____exit__ 方法(参见 docs)。

为了完整起见,在某些情况下您可能需要上下文管理器,但又不想为此定义整个 class。在那种情况下,contextlib may help.

一个有效的例子;说你有资源:

class Resource:

    def method(self):
        pass

get_resource = Resource
release_resource = lambda x: None

A RAII-like class 可能看起来像这样:

class RAIILike:

    def __init__(self):
        self.resource = get_resource()

    def __del__(self):
        release_resource(self.resource)

    def do_complex_thing(self):
        #  do something complex with resource
        pass

raii_thingy = RAIILike()

你会像这样使用资源:

raii_thingy.resource.method()

另一方面,上下文管理的资源可能如下所示...

class ContextManagedResource:

    def __enter__(self):
        self._resource = get_resource()
        return self._resource

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            #  handle exception here
            pass

        else:
            pass

        release_resource(self._resource)
        return True

...并像这样使用:

with ContextManagedResource() as res:
    res.method()

一旦with块结束,资源将自动释放,不管获取它的对象是否被垃圾回收.

你自己对维基百科的引用说:

Perl, Python (in the CPython implementation), and PHP manage object lifetime by reference counting, which makes it possible to use RAII. Objects that are no longer referenced are immediately destroyed or finalized and released, so a destructor or finalizer can release the resource at that time. However, it is not always idiomatic in such languages, and is specifically discouraged in Python (in favor of context managers and finalizers from the weakref package).

您可以在 python 中进行 RAII,或者非常接近。但是,与在构造函数和析构函数中执行工作的 C++ 不同,在 python 中,您需要使用 enterexit[=15 的 dunder 函数=].这个 post 写了一篇关于如何编写函数以及它们在出现异常时的行为方式的精彩文章:https://preshing.com/20110920/the-python-with-statement-by-example/