Метод __del__ не срабатывает вместе с методом __new__ в python

Метод __del__ не срабатывает вместе с методом __new__ в python, а отдельно очень даже хорошо работает. Т.е. я закомментирую __new__, __del__ работает, активирую - __new__ работает, __del__ нет.

class House:
    houses_history = []

    def __new__(cls, *args, **kwargs):
        cls.houses_history.append(args[0])

    def __init__(self, name, number_of_floors):
        self.name = name
        self.number_of_floors = number_of_floors

    def __del__(self):
        print(f'{self.name} снесён, но он останется в истории')

h1 = House('ЖК Эльбрус', 10)
print(House.houses_history)
h2 = House('ЖК Акация', 20)
print(House.houses_history)
h3 = House('ЖК Матрёшки', 20)
print(House.houses_history)

# Удаление объектов
del h2
del h3

print(House.houses_history)

Как это исправить?


Ответы (3 шт):

Автор решения: insolor

У вас объекты не создаются вообще (т.к. ваш переопределенный __new__ ничего не возвращает, а значит возвращает None), поэтому и удаляться нечему. Чтобы убедиться, что не создаются, достаточно через print вывести содержимое переменных h1, h2, h3 - там будет None (то что ваш __new__ вернул, то и записалось в переменные).

Чтобы код работал, нужно вызывать исходный __new__:

class House:
    houses_history = []

    def __new__(cls, *args, **kwargs):
        cls.houses_history.append(args[0])
        return super().__new__(cls)  # <- добавлено

    def __init__(self, name, number_of_floors):
        self.name = name
        self.number_of_floors = number_of_floors

    def __del__(self):
        print(f'{self.name} снесён, но он останется в истории')

h1 = House('ЖК Эльбрус', 10)
print(House.houses_history)
h2 = House('ЖК Акация', 20)
print(House.houses_history)
h3 = House('ЖК Матрёшки', 20)
print(House.houses_history)

# Удаление объектов
del h2
del h3

print(House.houses_history)

Вывод:

['ЖК Эльбрус']
['ЖК Эльбрус', 'ЖК Акация']
['ЖК Эльбрус', 'ЖК Акация', 'ЖК Матрёшки']
ЖК Акация снесён, но он останется в истории
ЖК Матрёшки снесён, но он останется в истории
['ЖК Эльбрус', 'ЖК Акация', 'ЖК Матрёшки']
ЖК Эльбрус снесён, но он останется в истории

В целом, оператор del не удаляет принудительно объект, а только освобождает указанную переменную (и таким образом уменьшает счетчик ссылок объекта на 1). Если объект окажется сохранен где-то еще, то он не будет удален, и __del__ не сработает.

Все __del__ точно сработают только при завершении программы, когда удалятся вообще все объекты (поэтому мы и видим удаление "ЖК Эльбрус" в конце, хотя del не вызывали).

→ Ссылка
Автор решения: popovic86

А принципиально использовать __new__? В принципе и через инициализатор можно добавить, результат вроде такой же или я ошибаюсь?

class House:
    houses_history = []


    # def __new__(cls, *args, **kwargs):
    #     cls.houses_history.append(args[0])
    #     return super().__new__(cls)  # <- добавлено

    def __init__(self, name, number_of_floors):
        self.name = name
        self.number_of_floors = number_of_floors
        House.houses_history.append(name)

    def __del__(self):
        print(f'{self.name} снесён, но он останется в истории')

h1 = House('ЖК Эльбрус', 10)
print(House.houses_history)
h2 = House('ЖК Акация', 20)
print(House.houses_history)
h3 = House('ЖК Матрёшки', 20)
print(House.houses_history)

# Удаление объектов
del h2
del h3

print(House.houses_history)
→ Ссылка
Автор решения: user605667

тут ещё вариант решения предложили - использовать декоратор @classmethod. Тогда так получается:

class House:

    houses_history = []

    def __new__(cls, *args, **kwargs):
        cls.houses_history.append(args[0])
        instance = object.__new__(cls) # добавили объект
        return instance

    def __init__(self, name, number_of_floors):
        self.name = name
        self.number_of_floors = number_of_floors

    def __del__(self):
        print(f'{self.name} снесён, но он останется в истории')

    @classmethod # Декоратор
    def get_houses_history(cls):
        return cls.houses_history
→ Ссылка