__ del __ метод, называемый в Python, когда это не ожидается

Я плохо знаком с Python и работал через примеры в CH Swaroop "Байт Python". Я вижу некоторое поведение с __del__ метод, который является озадачивающим меня.

В основном, если я запускаю следующий скрипт (в Python 2.6.2)

class Person4:
    '''Represents a person'''
    population = 0

    def __init__(self, name):
        '''Initialize the person's data'''
        self.name = name
        print 'Initializing %s'% self.name

        #When the person is created they increase the population
        Person4.population += 1

    def __del__(self):
        '''I am dying'''
        print '%s says bye' % self.name

        Person4.population -= 1

        if Person4.population == 0:
            print 'I am the last one'
        else:
            print 'There are still %d left' % Person4.population


swaroop = Person4('Swaroop')
kaleem = Person4('Kalem')

с помощью консоли Python (или Spyder интерактивная консоль) я вижу следующее:

execfile (u'C:\1_eric\Python\test1.py')
Инициализация Swaroop
Инициализация Kalem

execfile (u'C:\1_eric\Python\test1.py')
Инициализация Swaroop
Swaroop заявляет до свидания
Я - последний
Инициализация Kalem
Kalem заявляет до свидания
Я - последний

Почему __del__ метод сразу называют после __init__ на втором выполнении?
Я предполагаю, что, так как те же имена экземпляра ('swaroop' и 'kaleem') используются, что он выпускает исходный экземпляр и собирает "мусор" он. Но, это, кажется, играет опустошение с текущим количеством населения.

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

Спасибо, Eric

6
задан Bastien Léonard 20 December 2009 в 21:31
поделиться

2 ответа

Общий совет: не используйте __ del __ на Python. Это может нарушить сбор мусора несколькими способами, например, в случае круглых ссылок между объектами.

В вашем примере есть различные проблемы, связанные с использованием функции execfile() - что не является лучшей практикой - и переопределением глобальных переменных. Кстати, если вам действительно нужно создать псевдо-деструктор (т.е. код, который вызывается всякий раз, когда объект собирает мусор), напишите так называемую функцию "финализатор" (это некорректный деструктор) и вызовите ее, используя обратный вызов weakref.ref. Конечно, это не должно быть методом instance, и помните, что лямбда на самом деле создает закрытие, поэтому убедитесь, что в обратном вызове не будет утечки ссылок на себя! Если вам нужны данные из уничтоженного экземпляра, используйте подход с аргументами по умолчанию, просто убедитесь, что никогда не ссылается на "Я" внутри лямбды, иначе это не сработает. Вывод

from weakref import ref
from time import sleep

class Person4:
    '''Represents a person'''
    population = 0

    def __init__(self, name):
        '''Initialize the person's data'''
        self.name = name
        print 'Initializing %s'% self.name

        #When the person is created they increase the population
        Person4.population += 1

        self._wr = ref(self, lambda wr, name=self.name: Person4_finalizer(name))

def Person4_finalizer(name):
        '''I am dying'''
        print '%s says bye' % name

        Person4.population -= 1

        if Person4.population == 0:
            print 'I am the last one'
        else:
            print 'There are still %d left' % Person4.population

p1 = Person4("one")
p2 = Person4("two")
p3 = Person4("three")

del p2
del p3
sleep(5)

(сон там, чтобы помочь увидеть, что происходит):

Initializing one
Initializing two
Initializing three
two says bye
There are still 2 left
three says bye
There are still 1 left
one says bye
I am the last one
7
ответ дан 8 December 2019 в 04:08
поделиться

Здесь происходит несколько вещей. Когда ваш класс Person4 создается, он инициализирует свою переменную класса Population значением 0. С интерактивной консоли вы, кажется, запускаете файл «test1.py» несколько раз. При втором запуске класс Person4 объявляется снова, что делает его технически отличным от первого (даже несмотря на то, что у него такое же имя). Это означает, что у него есть свой собственный независимый счетчик населения .

Теперь swaroop и kaleem - это глобальные переменные, общие для обоих ваших экземпляры «test1.py». Python внутренне использует подсчет ссылок для большей части своей автоматической сборки мусора, поэтому исходный экземпляр первого класса Person4 не освобождается до второго присвоения swaroop . Присвоение swaroop уменьшает счетчик ссылок для первого экземпляра, вызывая вызов __ del __ , поскольку счетчик ссылок теперь равен нулю. Но поскольку вы ссылаетесь на Person4 по имени внутри __ del __ () , когда предыдущий экземпляр исчезает, он уменьшает новый Person4. счет населения вместо старого счетчика населения Person4

Надеюсь, это имело смысл. Я понимаю, почему это может сбить с толку тех, кто изучает Python. Использование вами переменных класса одновременно с переопределением класса Person4 с помощью execfile () еще больше сбивает с толку. Как бы то ни было, я написал много кода Python и не думаю, что мне когда-либо понадобилось использовать специальный метод __ del __ .

19
ответ дан 8 December 2019 в 04:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: