Как правильно очистить объект Python?
class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self) выше не исключение AttributeError. Я понимаю Python не гарантирует существование "глобальных переменных" (данные членов в этом контексте?), когда __del__() вызывается. Если это так, и это является причиной исключения, как я могу убедиться, что объект разрушается правильно?
6 ответов:
я бы рекомендовал использовать 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 будет экземпляром типа Package (это значение, возвращаемое
__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
как приложение к ответ Клинта, вы можете упростить
PackageResourceиспользуяcontextlib.contextmanager:@contextlib.contextmanager def packageResource(): class Package: ... package = Package() yield package package.cleanup()альтернативно, хотя, вероятно, не как 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): ...и
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 находится где-то еще (возможно, вы ошибочно удаляете себя.файл в другом месте).однако, как указывали другие, вы должны избегать использования
__del__. Основная причина этого заключается в том, что экземпляры с__del__не будет собираться мусор (они будут освобождены только тогда, когда их refcount достигнет 0). Поэтому, если ваш экземпляры участвуют в циклических ссылках, они будут жить в памяти до тех пор, пока приложение запускается. (Я могу ошибаться во всем этом, хотя мне придется снова прочитать документы gc, но я уверен, что это работает так).
стандартный способ-использовать
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...
Я думаю, что проблема может быть в
__init__Если есть больше кода, чем показано?
__del__будет вызываться даже тогда, когда__init__не был выполнен должным образом или вызвал исключение.
просто оберните деструктор с помощью оператора try/except, и он не будет выдавать исключение, если ваши глобалы уже удалены.
Edit
попробуйте это:
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он будет заполнять список файлов в del функция, которая гарантированно существует во время вызова. Прокси-сервер weakref должен предотвратить Python или себя от удаления себя.файлы переменной каким-то образом (если он будет удален, то это не повлияет на оригинал список файлов.) Если это не так, что это удаляется, даже если есть больше ссылок на переменную, то вы можете удалить инкапсуляцию прокси.
Comments