wil*_*ell 436 python destructor
class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
Run Code Online (Sandbox Code Playgroud)
__del__(self)上面因AttributeError异常而失败.我理解Python在__del__()调用时不保证存在"全局变量"(在此上下文中的成员数据?).如果是这种情况并且这是异常的原因,我该如何确保对象正确破坏?
Cli*_*ler 576
我建议使用Python的with语句来管理需要清理的资源.使用显式close()语句的问题在于,您必须担心人们忘记完全调用它或忘记将其放在finally块中以防止在发生异常时资源泄漏.
要使用该with语句,请使用以下方法创建一个类:
def __enter__(self)
def __exit__(self, exc_type, exc_value, traceback)
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,您将使用
class Package:
def __init__(self):
self.files = []
def __enter__(self):
return self
# ...
def __exit__(self, exc_type, exc_value, traceback):
for file in self.files:
os.unlink(file)
Run Code Online (Sandbox Code Playgroud)
然后,当有人想要使用你的课程时,他们会做以下事情:
with Package() as package_obj:
# use package_obj
Run Code Online (Sandbox Code Playgroud)
变量package_obj将是Package类型的实例(它是__enter__方法返回的值).__exit__无论是否发生异常,都会自动调用其方法.
您甚至可以将此方法更进一步.在上面的示例中,有人仍然可以使用其构造函数实例化Package而不使用该with子句.你不希望这种情况发生.您可以通过创建定义__enter__和__exit__方法的PackageResource类来解决此问题.然后,Package类将严格定义在__enter__方法内并返回.这样,调用者永远不会在不使用with语句的情况下实例化Package类:
class PackageResource:
def __enter__(self):
class Package:
...
self.package_obj = Package()
return self.package_obj
def __exit__(self, exc_type, exc_value, traceback):
self.package_obj.cleanup()
Run Code Online (Sandbox Code Playgroud)
您将使用如下:
with PackageResource() as package_obj:
# use package_obj
Run Code Online (Sandbox Code Playgroud)
ost*_*ach 39
标准方法是使用atexit.register:
# package.py
import atexit
import os
class Package:
def __init__(self):
self.files = []
atexit.register(self.cleanup)
def cleanup(self):
print("Running cleanup...")
for file in self.files:
print("Unlinking file: {}".format(file))
# os.unlink(file)
Run Code Online (Sandbox Code Playgroud)
但是你应该记住,这将持续所有创建的实例,Package直到Python终止.
使用上面的代码演示保存为package.py:
$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...
Run Code Online (Sandbox Code Playgroud)
Tob*_*ler 29
作为Clint答案的附录,您可以简化PackageResource使用contextlib.contextmanager:
@contextlib.contextmanager
def packageResource():
class Package:
...
package = Package()
yield package
package.cleanup()
Run Code Online (Sandbox Code Playgroud)
或者,虽然可能不像Pythonic,但您可以覆盖Package.__new__:
class Package(object):
def __new__(cls, *args, **kwargs):
@contextlib.contextmanager
def packageResource():
# adapt arguments if superclass takes some!
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
yield package
package.cleanup()
def __init__(self, *args, **kwargs):
...
Run Code Online (Sandbox Code Playgroud)
并简单地使用with Package(...) as package.
为了缩短范围,请命名清理函数close并使用contextlib.closing,在这种情况下,您可以使用未修改的Package类with contextlib.closing(Package(...))或__new__更改为更简单的类
class Package(object):
def __new__(cls, *args, **kwargs):
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
return contextlib.closing(package)
Run Code Online (Sandbox Code Playgroud)
并且这个构造函数是继承的,所以你可以简单地继承,例如
class SubPackage(Package):
def close(self):
pass
Run Code Online (Sandbox Code Playgroud)
Vir*_*ras 16
我不认为在__del__调用之前可以删除实例成员.我的猜测是你的特定AttributeError的原因是在其他地方(也许你错误地删除了其他地方的self.file).
但是,正如其他人指出的那样,你应该避免使用__del__.这样做的主要原因是__del__不会对垃圾实例进行垃圾收集(只有当它们的refcount达到0时才会释放它们).因此,如果您的实例涉及循环引用,则只要应用程序运行,它们就会存在内存中.(虽然我可能会误解所有这些,但我必须再次阅读gc文档,但我相信它会像这样工作).
Chr*_*ris 14
一个好主意是将这两种方法结合起来。
实现一个上下文管理器来进行显式的生命周期处理,并处理自动清理,以防用户忘记或不方便这样做,就像在解释器中一样。保证销毁时的清理最好由 完成weakref.finalize。
许多图书馆实际上就是这样做的。如果不使用上下文管理器,有些会发出警告,以鼓励人们使用它。
一旦设置好,一个好处weakref.finalize是,它的逻辑保证被调用一次。因此它可用于在所有情况下触发清理。
import os
from typing import List
import weakref
class Package:
def __init__(self):
self.files = []
self._finalizer = weakref.finalize(self, self._cleanup_files, self.files)
@staticmethod
def _cleanup_files(files: List):
for file in files:
os.unlink(file)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self._finalizer()
Run Code Online (Sandbox Code Playgroud)
weakref.finalize返回一个可调用的终结器对象,当 obj 被垃圾回收时将调用该对象。与普通的弱引用不同,终结器将始终存在,直到引用对象被收集为止。
一个缺点是,必须在静态方法内实现清理逻辑,因为此时对象不再存在。
这样做的一个好处atexit.register是,在解释器关闭之前,对象不会保留在内存中。当对象被垃圾回收时,清理就会发生。
与 不同的是object.__del__,weakref.finalize保证在解释器关闭时被调用,因为解释器会对它们进行特殊跟踪。
SCG*_*CGH 10
更好的选择是使用weakref.finalize.请参阅Finalizer对象中的示例和使用__del __()方法比较终结器.
只需用try/except语句包装你的析构函数,如果已经丢弃了全局变量,它就不会抛出异常.
编辑
试试这个:
from weakref import proxy
class MyList(list): pass
class Package:
def __init__(self):
self.__del__.im_func.files = MyList([1,2,3,4])
self.files = proxy(self.__del__.im_func.files)
def __del__(self):
print self.__del__.im_func.files
Run Code Online (Sandbox Code Playgroud)
它会将文件列表填入保证在调用时存在的del函数中.weakref代理是为了防止Python,或者你自己以某种方式删除self.files变量(如果它被删除,那么它不会影响原始文件列表).如果不是这种情况被删除,即使有更多的变量引用,那么您可以删除代理封装.
这是一个最小的工作框架:
class SkeletonFixture:
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def method(self):
pass
with SkeletonFixture() as fixture:
fixture.method()
Run Code Online (Sandbox Code Playgroud)
重要提示:自我回报
如果您像我一样,并且忽略了return self一部分(克林特·米勒的正确答案),那么您将盯着这个废话:
Traceback (most recent call last):
File "tests/simplestpossible.py", line 17, in <module>
fixture.method()
AttributeError: 'NoneType' object has no attribute 'method'
Run Code Online (Sandbox Code Playgroud)
我花了半天时间。希望它能帮助下一个人。
似乎这样做的惯用方法是提供一个close()方法(或类似方法),并明确地调用它。
| 归档时间: |
|
| 查看次数: |
399001 次 |
| 最近记录: |