如果要在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,它们也将被释放。
答案 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")
,则该属性有效。