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

时间:2018-11-02 13:02:49

标签: python resource-leak

如果要在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是Python GC管理的资源包装对象,而self._external_resource是Python GC以外的外部资源的句柄。

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

1 个答案:

答案 0 :(得分:4)

否,在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()方法中有以下注释:

  

这意味着_close由GC调用,self._python_resource可能是   已经被GC化,但是我们不确定,所以我们什么也不做,只能依靠   GC释放self._python_resource

我不确定您的意思是什么,但是只要您持有对对象的引用(并且只要它不是weak reference),就不会进行GC处理。 / p>

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

关于循环引用的注释3,:如果您有两个互相保持引用的对象,那么您已经形成了循环引用,因此两个对象不会被“常规”引用释放计数GC。取而代之的是,它们将由世代GC收集,除非除非采取__del__方法。 __del__禁止GC收集对象。参见gc.garbage

  

收集器发现无法访问但可以访问的对象列表   无法释放(无法收集的对象)。默认情况下,此列表   仅包含带有__del__() methods的对象。 [1]个具有   __del__()方法属于参考周期,导致整个参考周期无法收集,包括未必包含的对象   在周期中,但只能从周期中获得。

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

要点:don't use __del__

相关问题