Как правильно очистить объект Python?

class Package: def __init__(self): self.files = [] # ... def __del__(self): for file in self.files: os.unlink(file) 

__del__(self) выше завершается с ошибкой AttributeError. Я понимаю, что Python не гарантирует существование «глобальных переменных» (данные члена в этом контексте?) При __del__() . Если это так, и это причина исключения, как я могу убедиться, что объект деструктурирует правильно?

8 Solutions collect form web for “Как правильно очистить объект Python?”

Я бы рекомендовал использовать инструкцию Python для управления ресурсами, которые необходимо очистить. Проблема с использованием явного оператора 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__ и возвращен. Таким образом, вызывающий не смог создать экземпляр класса Package без использования инструкции 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 .

Чтобы сократить время, назовите функцию очистки и используйте contextlib.closing , и в этом случае вы можете либо использовать немодифицированный класс Package with 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 docs, но я уверен, что он работает так).

Я думаю, проблема может быть в __init__ если есть больше кода, чем показано?

__del__ будет вызываться, даже если __init__ не был выполнен правильно или __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 

Он будет заполнять список файлов в функции del, которая, как гарантируется, будет существовать во время вызова. Прокси-сервер weakref должен помешать Python или самостоятельно удалить переменную self.files (если она удалена, то это не повлияет на исходный список файлов). Если это не так, что это удаляется, хотя есть больше ссылок на переменную, вы можете удалить инкапсуляцию прокси.

Кажется, что идиоматический способ сделать это – предоставить метод close() (или аналогичный) и называть его явно.

Стандартный способ – использовать 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... 

Лучшей альтернативой является использование weakref.finalize . См. Примеры в объектах Finalizer и сравнение финализаторов с методами __del __ () .

Python - лучший язык программирования в мире.