我是否需要在 Python 中为资源包装器实现 Dispose 模式

Do I need to implement the Dispose Pattern for a resource wrapper in Python

如果我要在 Python 中实现安全资源包装器,我是否需要像 C# 一样实现 Dispose Pattern

这是我的意思的演示实现:

class ResourceWrapper:
    def __init__(self):
        self._python_resource = ...  # A Python object that manages some resources.
        self._external_resource = _allocate_resource()  # A resource handle to an external resource.
        self._is_closed = False  # Whether the object has been closed.

    def __del__(self):
        self._close(manual_close=False)  # Called by GC.

    def close(self):
        self._close(manual_close=True)  # Called by user to free resource early.

    def _close(self, manual_close):
        if not self._is_closed:  # Don’t want a resource to be closed more than once.
            if manual_close:
                # Since `_close` is called by user, we can guarantee that `self._python_resource` is still valid, so we
                # can close it safely.
                self._python_resource.close() 
            else:
                # This means `_close` is called by GC, `self._python_resource` might be already GCed, but we don’t know
                # for sure, so we do nothing and rely on GC to free `self._python_resource`.

                pass

            # GC will not take care of freeing unmanaged resource, so whether manual close or not, we have to close the
            # resource to prevent leaking.

            _free_resource(self._external_resource)

            # Now we mark the object as closed to prevent closing multiple times.

            self._is_closed = True

self._python_resource是由PythonGC管理的资源包装器对象,self._external_resource是不受PythonGC管理的外部资源的句柄。

我想确保在用户手册关闭包装器时释放托管和非托管资源,并且,如果包装器对象被 GC 处理,它们也会被释放。

不,在 Python 中你应该使用 Context Managers:

class ResourceWrapper:
    def __init__(self):
        ...

    ...


    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._close(manual_close=False)

with ResourceWrapper() as wrapper:
    # do something with wrapper

注1:_close()方法中有注释:

This means _close is called by GC, self._python_resource might be already GCed, but we don’t knowfor sure, so we do nothing and rely on GC to free self._python_resource.

我不确定你的意思,但只要你持有对对象的引用(只要它不是 weak reference),它就不会被 GC .

注释 2: 如果在没有 with 块的情况下使用作为上下文管理器的对象会怎样?然后当对象被垃圾收集时资源将被释放——但我不会担心。使用上下文管理器是 python 中的常见习语(请参阅带有 open()ing 文件的任何示例)。如果这对您的应用程序至关重要,您可以在 __enter__() 中获取资源,除非在 with 块中,否则不会获取资源。

注3,关于循环引用:如果你有两个对象相互引用,你就形成了循环引用,这样两个对象就不会被释放通过 "regular" 引用计数 GC。相反,它们将由分代 GC 收集,除非 碰巧有 __del__ 方法。 __del__ 禁止 GC 收集对象。见 gc.garbage:

A list of objects which the collector found to be unreachable but could not be freed (uncollectable objects). By default, this list contains only objects with __del__() methods. [1] Objects that have __del__() methods and are part of a reference cycle cause the entire reference cycle to be uncollectable, including objects not necessarily in the cycle but reachable only from it.

Python 3.4 引入了PEP-442,它引入了安全对象终结。无论哪种方式,您都不会有无效的引用。如果你有属性(hasattr(self, "_python_resource")),它将是有效的。

外卖:don't use __del__.