题
class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
上面失败的一个AttributeError例外。我明白了 蟒蛇不能保证 存在着"全球变量"(部件的数据,在这方面?) 时 __del__()
被援引。如果是这种情况下,这是因为例外,如何确定对象的毁否正确?
解决方案
我会建议使用Python的with
声明用于管理需要清理的资源。用一个明确的close()
声明的问题是,你不必担心忘记的人在所有调用或忘记把它放在一个finally
块,以防止资源泄漏时发生异常。
要使用with
语句,创建一类具有以下方法:
def __enter__(self)
def __exit__(self, exc_type, exc_value, traceback)
在你上面的例子,你会使用
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)
然后,当有人想使用你的类,他们会做到以下几点:
with Package() as package_obj:
# use package_obj
变量package_obj将型封装(它是由__enter__
方法返回的值)的一个实例。其__exit__
方法将被自动调用,而不管是否发生了异常。
您甚至可以采取这种做法更进了一步。在上面的例子中,有人仍然可以使用它的构造,而无需使用with
子句实例化包装。你不希望这样的事情发生。您可以通过创建一个PackageResource类定义__enter__
和__exit__
方法解决这个问题。然后,包类将进行严格的__enter__
方法内部定义并返回。这样一来,调用者永远无法实例化包装类,而无需使用with
语句:
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()
您会使用此如下:
with PackageResource() as package_obj:
# use package_obj
其他提示
的标准方法是使用 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)
但是你应该记住,这将持续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...
作为一个附录 克林特*的答案, 你可以简化 PackageResource
使用 contextlib.contextmanager
:
@contextlib.contextmanager
def packageResource():
class Package:
...
package = Package()
yield package
package.cleanup()
或者,虽然可能不如Python,你可以重写 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):
...
和简单的使用 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)
和这个构造是继承的,所以可以简单地继承的,例如
class SubPackage(Package):
def close(self):
pass
我不认为这是可能的__del__
被调用之前被删除实例成员。我的猜测是,为您的特定AttributeError的原因是在其他地方(也许你错误地删除self.file其他地方)。
不过,由于有人指出,应该避免使用__del__
。造成这种情况的主要原因是,随着__del__
情况下不会被垃圾收集(当其引用计数为0时,他们才会被释放)。因此,如果你的情况下参与循环引用,他们将生活在内存中,只要应用程序运行。 (我可能是弄错了这一切,虽然,我不得不再次读取GC文档,但我宁愿相信这是这样的)。
一个更好的选择是使用 weakref.最后确定.看到的例子 终结的对象 和 比较终结器与__德__()方法.
只是一个尝试换你的析构函数/ 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
这将在东西的德尔强>功能是保证在呼叫时存在的文件列表。该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()
重要的:的返回自强>
如果你像我一样,俯瞰着return self
部分(克林特·米勒的正确答案的),你会在这个无义盯着:
Traceback (most recent call last):
File "tests/simplestpossible.py", line 17, in <module>
fixture.method()
AttributeError: 'NoneType' object has no attribute 'method'
我花了半天这一点。希望它可以帮助下一个人。
看来,惯用的方法来做到这是提供一种方法close()
(或类似),并且明确地调用它。