class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
выше не удается с исключением AttributeError. Я понимаю, что Python не гарантирует существование "глобальных переменных" (данные члена в этом контексте?), Когда __del__()
вызывается. Если это так, и это является причиной исключения, как я могу убедиться, что объект разрушается должным образом?
python
destructor
wilhelmtell
источник
источник
__del__
не следует использовать в качестве аналога__init__
. (То есть, это не «деструктор» в том смысле, что__init__
это конструктор.Ответы:
Я бы рекомендовал использовать
with
оператор Python для управления ресурсами, которые необходимо очистить. Проблема с использованием явногоclose()
выражения состоит в том, что вам нужно беспокоиться о людях, которые вообще не будут вызывать его или забывают поместить его вfinally
блок, чтобы предотвратить утечку ресурсов при возникновении исключения.Чтобы использовать
with
оператор, создайте класс с помощью следующих методов:В вашем примере выше, вы бы использовали
Затем, когда кто-то захочет использовать ваш класс, он сделает следующее:
Переменная package_obj будет экземпляром типа Package (это значение, возвращаемое
__enter__
методом). Его__exit__
метод будет вызываться автоматически, независимо от того, происходит ли исключение.Вы могли бы даже сделать этот подход еще дальше. В приведенном выше примере кто-то еще может создать экземпляр Package, используя его конструктор, не используя
with
предложение. Вы не хотите, чтобы это произошло. Вы можете исправить это, создав класс PackageResource , который определяет__enter__
и__exit__
методы. Затем класс Package будет определен строго внутри__enter__
метода и возвращен. Таким образом, вызывающая сторона никогда не сможет создать экземпляр класса Package без использованияwith
инструкции:Вы бы использовали это следующим образом:
источник
with Resource(param1, param2) as r: # ...
Стандартный способ заключается в использовании
atexit.register
:Но вы должны иметь в виду, что это будет сохраняться во всех созданных экземплярах
Package
до тех пор, пока Python не будет завершен.Демонстрация с использованием приведенного выше кода, сохраненного как package.py :
источник
with
? Они явно вызывали__enter__
?) Недостатком является, конечно, если вам нужно, чтобы очистка произошла до python выходит, это не сработает. В моем случае мне все равно, когда объект выходит из области видимости или нет, пока Python не выйдет. :)atexit.register(self.__exit__)
?__exit__
и использовать контекстный менеджер? Кроме того,__exit__
принимает дополнительные аргументы (т. Е.__exit__(self, type, value, traceback)
), Так что вам придется учитывать их. В любом случае, похоже, что вы должны опубликовать отдельный вопрос по SO, потому что ваш вариант использования выглядит необычно?В качестве приложения к ответу Клинта , можно упростить с
PackageResource
помощьюcontextlib.contextmanager
:В качестве альтернативы, хотя, вероятно, не как Pythonic, вы можете переопределить
Package.__new__
:и просто использовать
with Package(...) as package
.Короче говоря, назовите свою функцию очистки
close
и используйтеcontextlib.closing
, в этом случае вы можете использовать неизмененныйPackage
класс черезwith contextlib.closing(Package(...))
или переопределить его__new__
на более простойИ этот конструктор наследуется, так что вы можете просто наследовать, например,
источник
Package.__new__()
метода. Или, может быть, мы можем. Возможно, мы могли бы определить декоратор класса или метакласс, обобщающий этот шаблон для нас. Пища для Питонической мысли.Package
также должен делать это (хотя я еще не проверял это), поэтому метакласс не требуется. Хотя в прошлом я нашел несколько довольно любопытных способов использования метаклассов ...Package
(или, точнее, класс с именемClosing
) в качестве родительского класса вместоobject
. Но не спрашивайте меня, как множественное наследование портится с этим ...Я не думаю, что члены экземпляра могут быть удалены до
__del__
вызова. Я предполагаю, что причина вашего конкретного AttributeError находится где-то еще (возможно, вы ошибочно удалили self.file в другом месте).Однако, как отметили другие, вы должны избегать использования
__del__
. Основная причина этого заключается в том, что экземпляры с__del__
не будут собирать мусор (они будут освобождены только тогда, когда их рефконт достигнет 0). Поэтому, если ваши экземпляры участвуют в циклических ссылках, они будут жить в памяти до тех пор, пока выполняется приложение. (Хотя я могу ошибаться во всем этом, мне придется снова прочитать документы gc, но я уверен, что это работает так).источник
__del__
могут быть собраны мусором, если их счетчик ссылок от других объектов с__del__
нуля, и они недоступны. Это означает, что если у вас есть ссылочный цикл между объектами__del__
, ни один из них не будет собран. Любой другой случай, однако, должен быть решен как ожидалось.Лучшая альтернатива - это использовать weakref.finalize . См. Примеры в разделах Объекты финализатора и Сравнение финализаторов с методами __del __ () .
источник
stop()
метод для закрытия портов иjoin()
процессов. Однако, если программы выходят неожиданноstop()
, не вызывается - я решил это с помощью финализатора. Но в любом случае я вызываю_finalizer.detach()
метод stop, чтобы предотвратить его повторный вызов (вручную и позже снова финализатором).Я думаю, что проблема может быть в
__init__
если есть больше кода, чем показано?__del__
будет называться даже когда__init__
он не был выполнен должным образом или выдал исключение.Источник
источник
__del__
- это явно объявить все члены на уровне класса, гарантируя, что они всегда существуют, даже если происходит__init__
сбой. В данном примереfiles = ()
будет работать, хотя в основном вы просто назначитеNone
; в любом случае вам все еще нужно присвоить реальное значение в__init__
.Вот минимальный рабочий скелет:
Важно: вернуть себя
Если вы похожи на меня и пропускаете
return self
часть ( правильного ответа Клинта Миллера ), вы будете пялиться на эту чушь:Надеюсь, это поможет следующему человеку.
источник
Просто оберните ваш деструктор оператором try / исключением, и он не выдаст исключение, если ваши глобальные переменные уже удалены.
редактировать
Попробуй это:
Он поместит список файлов в функцию del, которая гарантированно существует во время вызова. Прозрачный прокси-сервер запрещает Python или вам самим каким-либо образом удалять переменную self.files (если она будет удалена, это не повлияет на исходный список файлов). Если это не тот случай, когда он удаляется, хотя ссылок на переменную больше, вы можете удалить инкапсуляцию прокси.
источник
Кажется, что идиоматический способ сделать это состоит в том, чтобы предоставить
close()
метод (или подобный), и вызвать его явно.источник