Деструктор в Python – это специальный метод, который вызывается при уничтожении объекта. Конструктор же, наоборот, используется для создания и инициализации объекта класса.
Друзья, подписывайтесь на наш телеграм канал Pythonist. Там еще больше туториалов, задач и книг по Python.
В этой статье мы разберем:
__del__()
В объектно-ориентированном программировании деструктор вызывается при удалении или уничтожении объекта. Деструктор используется для выполнения действий по очистке перед разрушением объекта, таких как закрытие соединений с базой данных или дескриптор файла.
В Python есть сборщик мусора, который автоматически обрабатывает управление памятью. Например, он очищает память, когда объект выходит за пределы области видимости.
Однако при уничтожении объекта необходимо освобождать не только память. Мы должны освободить или закрыть другие ресурсы, которые использовались объектом, такие как открытые файлы, соединения с базой данных, очистка буфера или кеша. Для выполнения всех этих задач очистки мы используем деструктор в Python.
Деструктор противоположен конструктору. Конструктор используется для инициализации объектов, а деструктор — для удаления или уничтожения объектов, в результате чего освобождается ресурс, занятый этими объектами.
В Python деструктор вызывается не вручную, а полностью автоматически. Это происходит в следующих двух случаях:
Для определения деструктора используется специальный метод __del__()
. Например, когда мы выполняем del имя_объекта
, деструктор вызывается автоматически, и объект собирается в мусор.
Магический метод __del__()
используется как деструктор в Python. Метод __del__()
будет неявно вызываться, когда все ссылки на объект будут удалены, то есть когда объект подходит для сборщика мусора.
Этот метод автоматически вызывается в Python, когда экземпляр собираются уничтожить. Его также называют финализатором или (неправильно) деструктором.
Синтаксис объявления деструктора будет следующим:
def __del__(self): # тело деструктора
Здесь:
def
– ключевое слово, которое используется для определения метода.__del__()
– зарезервированный метод. Он вызывается, как только все ссылки на объект будут удалены.self
: первый аргумент self
относится к текущему объекту.Примечание. Аргументы метода __del__()
необязательны. Мы можем определить деструктор с любым количеством аргументов.
Давайте рассмотрим создание деструктора в Python на простом примере. Мы создадим класс Student
с деструктором.
class Student: # конструктор def __init__(self, name): print('Inside Constructor') self.name = name print('Object initialized') def show(self): print('Hello, my name is', self.name) # деструктор def __del__(self): print('Inside destructor') print('Object destroyed') # создать объект s1 = Student('Emma') s1.show() # удалить объект del s1
Запустим наш код и получим следующий результат:
Inside Constructor Object initialized Hello, my name is Emma Inside destructor Object destroyed
Примечание. Как видно из вывода, при удалении ссылки на объект с помощью del s1
метод __del__()
вызывается автоматически.
В приведенном выше коде мы создали один объект. s1
– это ссылочная переменная, указывающая на вновь созданный объект.
Деструктор вызывается, когда ссылка на объект удалена или счетчик ссылок на объект доходит до нуля.
[python_ad_block]__del__()
вызывается для любого объекта, когда счетчик ссылок для этого объекта становится равным нулю.del
.Давайте разберемся в приведенных выше пунктах на примере.
Сначала создадим объект класса Student
, используя s1 = student ('Emma')
.
Затем давайте создадим новую ссылку на объект, присвоив переменной s2
значение s1
(т.е. s2 = s1
).
Теперь обе ссылочные переменные s1
и s2
указывают на один и тот же объект.
От редакции Pythonist. Ссылки, имена и значения подробно рассмотрены в статье «Факты и мифы об именах и значениях в Python».
Далее мы удалим ссылку s1
.
Затем добавим 5 секунд задержки (sleep) к основному потоку, чтобы было ясно, что деструкторы вызываются только при удалении всех ссылок на объекты.
import time class Student: # конструктор def __init__(self, name): print('Inside Constructor') self.name = name def show(self): print('Hello, my name is', self.name) # деструктор def __del__(self): print('Object destroyed') # создание объекта s1 = Student('Emma') # создание новой ссылки # обе ссылки указывают на один объект s2 = s1 s1.show() # удаление ссылки s1 del s1 # добавление задержки и наблюдение за результатом time.sleep(5) print('After sleep') s2.show()
Наш вывод:
Inside Constructor Hello, my name is Emma
После паузы:
After sleep Hello, my name is Emma Object destroyed
s2
вручную с помощью del s2
. Это произошло автоматически, т.к. программа закончилась.__del__()
не является идеальным решением для очистки ненужных объектов. В Python деструктор ведет себя странно и не выполняется в следующих двух случаях:
__init__()
__del__()
некорректно работает в случае циклической ссылки.Суть этой ссылки в том, что два объекта ссылаются друг на друга. И когда оба объекта выходят за пределы области видимости, Python не знает, какой объект уничтожить первым. Поэтому, чтобы избежать ошибок, он не уничтожает ни один из них.
Короче говоря, это означает, что сборщик мусора не знает порядок, в котором объекты должны быть уничтожены, поэтому он не удаляет их из памяти.
В идеале деструктор должен выполняться, когда объект выходит за пределы области видимости или его счетчик ссылок достигает нуля. Но объекты, связанные круговой ссылкой, будут храниться в памяти до тех пор, пока приложение будет работать.
В приведенном ниже примере в идеале оба объекта — Vehicle
и Car
— должны быть уничтожены сборщиком мусора после выхода за пределы области видимости. Тем не менее, из-за циклической ссылки они остаются в памяти.
Для управления ресурсами, которые необходимо очистить, можно посоветовать использовать оператор with
.
import time class Vehicle(): def __init__(self, id, car): self.id = id; # сохранение ссылки объекта Car self.dealer = car; print('Vehicle', self.id, 'created'); def __del__(self): print('Vehicle', self.id, 'destroyed'); class Car(): def __init__(self, id): self.id = id; # сохранение объекта класса Vehicle в переменной 'dealer' # пересылка ссылки объекта Car ('self') для объекта Vehicle self.dealer = Vehicle(id, self); print('Car', self.id, 'created') def __del__(self): print('Car', self.id, 'destroyed') # создание объекта car c = Car(12) # удаление объекта car del c # в идеале теперь должен выполниться деструктор # задержка для наблюдения за поведением time.sleep(8)
Запустим наш код и получим следующее:
Vehicle 12 created Car 12 created
В объектно-ориентированном программировании конструктор – это специальный метод, используемый для создания и инициализации объекта класса. Используя метод __init__()
, мы можем реализовать конструктор для инициализации объекта.
В ООП, если в конструкторе возникает какое-либо исключение при инициализации объекта, конструктор уничтожает объект.
Аналогично, в Python, если в методе инициализации возникает какое-либо исключение при инициализации объекта, вызывается метод del
. Но на самом деле объект не создается, и ему не выделяются ресурсы.
Несмотря на то, что объект так и не был инициализирован правильно, метод del
все равно попытается очистить все ресурсы. А это, в свою очередь, может привести к другому исключению.
class Vehicle: def __init__(self, speed): if speed > 240: raise Exception('Not Allowed'); self.speed = speed; def __del__(self): print('Release resources') # создание объекта car = Vehicle(350); # для явного удаления объекта: del car
Запустив этот код, мы получим следующий результат:
Traceback (most recent call last): Release resources Exception: Not Allowed
В объектно-ориентированном программировании деструктор вызывается при удалении или уничтожении объекта.
Деструктор используется для выполнения действий по очистке перед разрушением объекта, таких как закрытие соединений с базой данных.
Для выполнения задачи очистки перед удалением объекта в Python мы используем метод __del__()
.
При удалении ссылки на объект деструктор не запускается. Для этого нужно удалить все ссылки на этот объект.
Перевод статьи «Python Destructors to Destroy the Object».
Управление памятью - важный, но часто упускаемый из виду аспект программирования. При неправильном подходе оно…
Как возникает круговой импорт? Эта ошибка импорта обычно возникает, когда два или более модуля, зависящих…
Вы когда-нибудь оказывались в ситуации, когда скрипт на Python выполняется очень долго и вы задаетесь…
В этом руководстве мы разберем все, что нужно знать о символах перехода на новую строку…
Блок if __name__ == "__main__" в Python позволяет определить код, который будет выполняться только при…
Давайте разберем, как настроить модульные тесты для экземпляров классов. Мы напишем тесты для проверки функциональности…