首页 > 解决方案 > 资源获取是初始化,在 Python 中

问题描述

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

在一些代码审查中,我有几个同行希望我将事情从initdel转移到 start 和 stop 方法。大多数情况下,这与几十年来 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 资源已发布,对吗?

标签: pythonraii

解决方案


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

在像 Python 这样的垃圾收集语言中,理论上您的对象永远不会被销毁,即使您调用del它也是如此

无论如何,在 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__方法的类的实例(请参阅文档)。

为了完整起见,在某些情况下您可能需要一个上下文管理器,但又不想为此定义整个类。在那种情况下,contextlib 可能会有所帮助

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

class Resource:

    def method(self):
        pass

get_resource = Resource
release_resource = lambda x: None

类似 RAII 的类可能看起来像这样:

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块结束,资源将被自动释放,无论获取它的对象是否被垃圾回收


推荐阅读