Python деструктор не работает

Содержание
  1. Деструктор в Python: уничтожение объектов
  2. Что такое деструктор в Python?
  3. Создание деструктора с помощью метода __del__()
  4. Пример
  5. Марк Лутц «Изучаем Python»
  6. Важные моменты, которые следует помнить о деструкторе
  7. Пример
  8. Случаи, когда деструктор работает не корректно
  9. Круговая ссылка
  10. Пример
  11. Исключение в методе __init__()
  12. Пример
  13. Заключение
  14. Python-сообщество
  15. Уведомления
  16. #1 Март 2, 2013 12:01:40
  17. Не вызывается деструктор самописного класса при работе с Tkinter
  18. #2 Март 2, 2013 16:20:42
  19. Не вызывается деструктор самописного класса при работе с Tkinter
  20. #3 Март 2, 2013 16:35:19
  21. Не вызывается деструктор самописного класса при работе с Tkinter
  22. #4 Март 2, 2013 16:48:36
  23. Не вызывается деструктор самописного класса при работе с Tkinter
  24. #5 Март 2, 2013 16:52:10
  25. Не вызывается деструктор самописного класса при работе с Tkinter
  26. #6 Март 2, 2013 17:22:05
  27. Не вызывается деструктор самописного класса при работе с Tkinter
  28. #7 Март 2, 2013 19:20:12
  29. Не вызывается деструктор самописного класса при работе с Tkinter
  30. #8 Март 2, 2013 23:11:46
  31. Не вызывается деструктор самописного класса при работе с Tkinter
  32. #9 Март 2, 2013 23:17:00
  33. Не вызывается деструктор самописного класса при работе с Tkinter
  34. #10 Март 3, 2013 03:45:15
  35. Не вызывается деструктор самописного класса при работе с Tkinter
  36. Python-сообщество
  37. Уведомления
  38. #1 Март 1, 2013 21:57:49
  39. Не вызывается деструктор самописного класса при работе с Tkinter
  40. #2 Март 1, 2013 22:19:53
  41. Не вызывается деструктор самописного класса при работе с Tkinter
  42. #3 Март 1, 2013 22:35:35
  43. Не вызывается деструктор самописного класса при работе с Tkinter
  44. #4 Март 1, 2013 23:33:34
  45. Не вызывается деструктор самописного класса при работе с Tkinter
  46. #5 Март 2, 2013 00:06:36
  47. Не вызывается деструктор самописного класса при работе с Tkinter

Деструктор в Python: уничтожение объектов

Деструктор в Python – это специальный метод, который вызывается при уничтожении объекта. Конструктор же, наоборот, используется для создания и инициализации объекта класса.

В этой статье мы разберем:

  • как создать деструктор в Python
  • использование метода __del__()
  • как работает деструктор.

Что такое деструктор в Python?

В объектно-ориентированном программировании деструктор вызывается при удалении или уничтожении объекта. Деструктор используется для выполнения действий по очистке перед разрушением объекта, таких как закрытие соединений с базой данных или дескриптор файла.

В Python есть сборщик мусора, который автоматически обрабатывает управление памятью. Например, он очищает память, когда объект выходит за пределы области видимости.

Однако при уничтожении объекта необходимо освобождать не только память. Мы должны освободить или закрыть другие ресурсы, которые использовались объектом, такие как открытые файлы, соединения с базой данных, очистка буфера или кеша. Для выполнения всех этих задач очистки мы используем деструктор в Python.

Деструктор противоположен конструктору. Конструктор используется для инициализации объектов, а деструктор — для удаления или уничтожения объектов, в результате чего освобождается ресурс, занятый этими объектами.

В Python деструктор вызывается не вручную, а полностью автоматически. Это происходит в следующих двух случаях:

  • когда объект выходит за пределы области видимости
  • когда счетчик ссылок на объект достигает 0.

Для определения деструктора используется специальный метод __del__() . Например, когда мы выполняем del имя_объекта , деструктор вызывается автоматически, и объект собирается в мусор.

Создание деструктора с помощью метода __del__()

Магический метод __del__() используется как деструктор в Python. Метод __del__() будет неявно вызываться, когда все ссылки на объект будут удалены, то есть когда объект подходит для сборщика мусора.

Этот метод автоматически вызывается в Python, когда экземпляр собираются уничтожить. Его также называют финализатором или (неправильно) деструктором.

Синтаксис объявления деструктора будет следующим:

  • def – ключевое слово, которое используется для определения метода.
  • __del__() – зарезервированный метод. Он вызывается, как только все ссылки на объект будут удалены.
  • self : первый аргумент self относится к текущему объекту.

Примечание. Аргументы метода __del__() необязательны. Мы можем определить деструктор с любым количеством аргументов.

Пример

Давайте рассмотрим создание деструктора в Python на простом примере. Мы создадим класс Student с деструктором.

Запустим наш код и получим следующий результат:

Примечание. Как видно из вывода, при удалении ссылки на объект с помощью del s1 метод __del__() вызывается автоматически.

В приведенном выше коде мы создали один объект. s1 – это ссылочная переменная, указывающая на вновь созданный объект.

Деструктор вызывается, когда ссылка на объект удалена или счетчик ссылок на объект доходит до нуля.

Читайте также:  Если некачественно отремонтировали автомобиль куда обращаться

Марк Лутц «Изучаем Python»

Скачивайте книгу у нас в телеграм

Важные моменты, которые следует помнить о деструкторе

  • Метод __del__() вызывается для любого объекта, когда счетчик ссылок для этого объекта становится равным нулю.
  • Счетчик ссылок для данного объекта становится нулевым, когда работа программы завершается, или мы удаляем все ссылки вручную с помощью ключевого слова del .
  • Деструктор не будет запускаться при удалении какой-то одной ссылки на объект. Он будет вызываться только тогда, когда все ссылки на объект будут удалены.

Пример

Давайте разберемся в приведенных выше пунктах на примере.

Сначала создадим объект класса Student , используя s1 = student (‘Emma’) .

Затем давайте создадим новую ссылку на объект, присвоив переменной s2 значение s1 (т.е. s2 = s1 ).

Теперь обе ссылочные переменные s1 и s2 указывают на один и тот же объект.

От редакции Pythonist. Ссылки, имена и значения подробно рассмотрены в статье «Факты и мифы об именах и значениях в Python».

Далее мы удалим ссылку s1 .

Затем добавим 5 секунд задержки (sleep) к основному потоку, чтобы было ясно, что деструкторы вызываются только при удалении всех ссылок на объекты.

  • Как вы можете видеть из полученного результата, деструкторы вызываются только тогда, когда удаляются все ссылки на объекты.
  • Кроме того, деструктор выполняется, когда код (программа) заканчивается и объект становится доступным для сборщика мусора. Например, мы не удаляли ссылку на объект s2 вручную с помощью del s2 . Это произошло автоматически, т.к. программа закончилась.

Случаи, когда деструктор работает не корректно

__del__() не является идеальным решением для очистки ненужных объектов. В Python деструктор ведет себя странно и не выполняется в следующих двух случаях:

  • ссылка является круговой: два объекта ссылаются друг на друга
  • исключение в методе __init__()

Круговая ссылка

__del__() некорректно работает в случае циклической ссылки.Суть этой ссылки в том, что два объекта ссылаются друг на друга. И когда оба объекта выходят за пределы области видимости, Python не знает, какой объект уничтожить первым. Поэтому, чтобы избежать ошибок, он не уничтожает ни один из них.

Короче говоря, это означает, что сборщик мусора не знает порядок, в котором объекты должны быть уничтожены, поэтому он не удаляет их из памяти.

В идеале деструктор должен выполняться, когда объект выходит за пределы области видимости или его счетчик ссылок достигает нуля. Но объекты, связанные круговой ссылкой, будут храниться в памяти до тех пор, пока приложение будет работать.

Пример

В приведенном ниже примере в идеале оба объекта — Vehicle и Car — должны быть уничтожены сборщиком мусора после выхода за пределы области видимости. Тем не менее, из-за циклической ссылки они остаются в памяти.

Для управления ресурсами, которые необходимо очистить, можно посоветовать использовать оператор with .

Запустим наш код и получим следующее:

Исключение в методе __init__()

В объектно-ориентированном программировании конструктор – это специальный метод, используемый для создания и инициализации объекта класса. Используя метод __init__() , мы можем реализовать конструктор для инициализации объекта.

В ООП, если в конструкторе возникает какое-либо исключение при инициализации объекта, конструктор уничтожает объект.

Аналогично, в Python, если в методе инициализации возникает какое-либо исключение при инициализации объекта, вызывается метод del . Но на самом деле объект не создается, и ему не выделяются ресурсы.

Несмотря на то, что объект так и не был инициализирован правильно, метод del все равно попытается очистить все ресурсы. А это, в свою очередь, может привести к другому исключению.

Пример

Запустив этот код, мы получим следующий результат:

Заключение

В объектно-ориентированном программировании деструктор вызывается при удалении или уничтожении объекта.

Деструктор используется для выполнения действий по очистке перед разрушением объекта, таких как закрытие соединений с базой данных.

Для выполнения задачи очистки перед удалением объекта в Python мы используем метод __del__() .

При удалении ссылки на объект деструктор не запускается. Для этого нужно удалить все ссылки на этот объект.

Источник

Python-сообщество

Уведомления

#1 Март 2, 2013 12:01:40

Не вызывается деструктор самописного класса при работе с Tkinter

Простите, это так мило за вами наблюдать. Но все же позвольте вставить свои 5 копеек.

__del__ это НЕ деструктор. Точне хоть в документации его так и называют, это не деструктор в привычном его понимании.

Во-первых время вызова этого метода не определено. Он может быть вызван как сразу после того как будет уничтожена последняя ссылка на объект, так и несколько позже. А может быть вообще не вызван.

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

Поэтому использовать метод __del__ для подчистки ресурсов категорически нельзя. Кроме этого с методом __del__ связано еще несколько нюансов с управлением памятью. И без крайней на то необходимости, кроме случая когда Вы точно уверены, что этот метод вам нужен, его обычно не используют.

Читайте также:  Как настроить экран старого монитора

Если необходимо ограничить время существования объекта и подчистить ресурсы за ним, лучше использовать context managers (конструкция with) или явно вызывать метод когда объект более не нужен.

Отредактировано cutwater (Март 2, 2013 12:03:20)

#2 Март 2, 2013 16:20:42

Не вызывается деструктор самописного класса при работе с Tkinter

cutwater
Если я не ошибаюсь Вы уже об этом писали в блоге по вопросам with и garbage collection. Кроме того, как говориться в правилах “Явное лучше неявного…”.
P.S. Сказали, что пять копеек, а насыпали как на рубль 🙂
P.S.S. На самом деле здесь как проблема так и задача была иного характера, только косвенно связанноя с деструктором… Проблемой было использование неподходящего метода виджета.

Отредактировано 4kpt (Март 2, 2013 16:37:07)

#3 Март 2, 2013 16:35:19

Не вызывается деструктор самописного класса при работе с Tkinter

Dwarf
Последний раз говорю, что не буду отвечать, если тему не перенесете или не продублируете в GUI.

#4 Март 2, 2013 16:48:36

Не вызывается деструктор самописного класса при работе с Tkinter

4kpt, заканчивайте с Вашей политикой переноса или “дублирования” тем.
Если надо перенести просите модераторов. И за пропаганду дублирования тем — предупреждение! Это уже далеко не первый раз.

По поводу ответа, я всю тему не читал. Я лишь обратил внимание на явное заблуждение товарища ТС по поводу __del__ и непонимания как он работает.

Отредактировано cutwater (Март 2, 2013 16:50:03)

#5 Март 2, 2013 16:52:10

Не вызывается деструктор самописного класса при работе с Tkinter

Не знал, что темы нельзя дублировать…
Впредь не повторится.

P.S. Модераторов просить не буду. Это и так должно выполняться 🙂

#6 Март 2, 2013 17:22:05

Не вызывается деструктор самописного класса при работе с Tkinter

Темы дублировать нежелательно. Если необходимо или по решению модератора тема может быть перемещена в соответствующий раздел.

#7 Март 2, 2013 19:20:12

Не вызывается деструктор самописного класса при работе с Tkinter

cutwater

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

#8 Март 2, 2013 23:11:46

Не вызывается деструктор самописного класса при работе с Tkinter

Как бы объект и занимаемая им память совершенно не связанные между собой понятия. При чем операционная система по завершении процесса освобождает занимаемую им память.

А вот вызывать код, который корректно освободит ресурсы, запишет состояние и т.п. нужно ручками. И полагаться тут на финализаторы (деструкторы) что в C#, Java, Python нельзя.
Тут поведение отлично от С++.

#9 Март 2, 2013 23:17:00

Не вызывается деструктор самописного класса при работе с Tkinter

Как бы объект и занимаемая им память совершенно не связанные между собой понятия.

Но они же взаимосвязаны? Есть объект -> значит есть занятая им память. Нет объекта — нет памяти. Разве нет?

При чем операционная система по завершении процесса освобождает занимаемую им память.

А вот вызывать код, который корректно освободит ресурсы, запишет состояние и т.п. нужно ручками.

#10 Март 3, 2013 03:45:15

Не вызывается деструктор самописного класса при работе с Tkinter

Dwarf
То есть это нормальное поведение в Python, когда в некоторых условиях деструктор так никогда не будет вызван? Т.е. это не баг, вот что я хочу понять.

Документация вполне определенно описывает данное поведение. Так что это никакой не баг.

Dwarf
Но они же взаимосвязаны? Есть объект -> значит есть занятая им память. Нет объекта — нет памяти. Разве нет?

Отредактировано cutwater (Март 3, 2013 03:53:29)

Источник

Python-сообщество

Уведомления

#1 Март 1, 2013 21:57:49

Не вызывается деструктор самописного класса при работе с Tkinter

Есть самописный класс (config), отвечающий за хранение кое-каких настроек. В его деструкторе (метод __del__()) реализовано сохранение в файл.

Читайте также:  Better font picker figma не работает

Также есть класс, рисующий GUI на tkinter (gui). В нём на событие закрытия окна забинден отдельный метод, который выполняет сохранение в объект класса настроек с последующим выходом из программы.

Так метод забиден

(почему с задержкой 1000 — потому что это костыль ради обхода ещё одной непонятной мне проблемы)

Я создаю объект вышеупомянутого config и передаю его в конструктор gui, чтобы тот считывал и записывал в него настройки.
Но когда я закрываю окно tkinter’а (стандартно, в Windows, путём нажатия на красный крестик) и программа завершается с кодом 0 (т.е. всё в порядке) — сохранения конфига в файл не происходит.
Но если я вставляю в самом конце программы вызов деструктора вручную, то настройки нормально сохраняются:

Отредактировано Dwarf (Март 1, 2013 22:01:10)

#2 Март 1, 2013 22:19:53

Не вызывается деструктор самописного класса при работе с Tkinter

Где Вы взяли такую конструкцию.

Зачем она нужна?
Каким образом можно еще выполнить выход из программы или закрытие базового окна?
Во вторых. Каким образом Вы передаете свой config в GUI? Возможно в этом кроется ошибка… Очень сумбурно. Как функция закрытия знает о существовании config?

P.S. Возможно это поможет Вам ответить на Ваш вопрос.

Отредактировано 4kpt (Март 1, 2013 22:31:30)

#3 Март 1, 2013 22:35:35

Не вызывается деструктор самописного класса при работе с Tkinter

Где Вы взяли такую конструкцию. Зачем она нужна?

Всё равно, проблема с сохранением конфига остаётся.

Каким образом можно еще выполнить выход из программы или закрытие базового окна?

Не совсем понял к чему это. Или вы не знаете тонкостей tkinter? По умолчанию там закрытие работает само собой. Но мне вместо с закрытием нужно провести определенные действия, поэтому я написал свой обработчик.

Во вторых. Каким образом Вы передаете свой config в GUI?

Отредактировано Dwarf (Март 1, 2013 22:38:32)

#4 Март 1, 2013 23:33:34

Не вызывается деструктор самописного класса при работе с Tkinter

Во первых. Если Вы в программе прикручиваете такие костыли — это Вы не знаете не то что тонкостей, а и основ работы Tkinter. То что Вы написали используется крайне редко и со значениями далекими от 1000 :). И я практически уверен, что у Вас не тот случай.
Во-вторых. По вашему вопросу. Если код структурно соответствует тому, что Вы написали, то тогда все работает…

Отредактировано 4kpt (Март 1, 2013 23:54:02)

#5 Март 2, 2013 00:06:36

Не вызывается деструктор самописного класса при работе с Tkinter

Если Вы в программе прикручиваете такие костыли

Стараюсь без них. Пока у меня в программе только две странности (и соответственно 2 костыля). Одна из них — указана в этом топике.

А костыль Вы прикрутили, так как окно не хотело закрываться после нажатия на крестик…

Поясняю — у меня есть ещё класс Grid, наследующий Frame. В этом классе я компоную Entry и Canvas’ы в самодельную таблицу, а потом эту таблицу (объект класса Grid) вставляю в главное окно. Это происходит в классе Gui.

Так вот в Grid у меня есть функция, в которой работает бесконечный цикл, занимающийся приёмом сообщений по очереди (стандартный питоновский Queue) с последующим отражением информации, извлеченной из этих сообщений, в таблице.

По умолчанию, если этот бесконечный цикл работает — программа на нажатие красного крестика вообще не реагирует, несмотря на то, что в цикле стоят
self.root.update_idletasks()
self.root.update()
Благодаря им, понятное дело, интерфейс остаётся отзывчивым, вот только не закрывается.
Тогда я классе Grid реализовал внутреннюю переменную-флаг, который означает, что из цикла нужно выйти, и на каждой итерации проверяю его. Для установки этой переменной в Grid реализована функция total_exit(), которая ставит её в True.

Думаю вы уже догадались, что я вызываю эту функцию из обработчика закрытия главного окна, т.е. из вышеупомянутой _update_config_and_close()

Проблема в том, выход из цикла происходит не мгновенно (пока очередная итерация дойдёт до проверки переменной-флага, пока выйдет), и поэтому в _update_config_and_close() нужна пауза между вызовом total_exit() и непосредственно закрытием окна через root.quit(). Если эту паузу не реализовать — на кнопку закрытия окна приходится жать дважды, т.к. пока выход из цикла не произведен — вызов root.quit() игнорируется.

Отредактировано Dwarf (Март 2, 2013 00:18:12)

Источник

Оцените статью