我是否需要在 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__
.
如果我要在 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 freeself._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__
.