_метод _del__ вызывается в python, когда этого не ожидается

StackOverflow https://stackoverflow.com/questions/1935153

  •  20-09-2019
  •  | 
  •  

Вопрос

Я новичок в python и работал с примерами из книги Swaroop CH "A Byte of 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) Я вижу следующее:

исполнительный файл (u'C:\1_eric\Python est1.py')
Инициализация Swaroop
Инициализация Kalem

исполнительный файл (u'C:\1_eric\Python est1.py')
Инициализация Swaroop
Сваруп говорит "пока"
Я - последний
Инициализация Kalem
Калем говорит "пока"
Я - последний

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

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

Спасибо, Эрик

Это было полезно?

Решение

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

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

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

Другие советы

Здесь происходит пара вещей.Когда ваш Person4 создается экземпляр класса, он инициализирует свой population переменная класса равна 0.Из интерактивной консоли, вы, кажется управлении "test1.py" файл несколько раз.Во второй раз, когда вы запустите его, Person4 класс объявляется снова, что делает его технически другой из первого (хотя у него такое же название).Это означает, что у него есть свой собственный независимый population считай.

Сейчас, swaroop и kaleem являются глобальный переменные, общие для обоих ваших экземпляров "test1.py".Python внутренне использует подсчет ссылок для большей части своей автоматической сборки мусора, поэтому исходный экземпляр первого Person4 класс не освобождается до тех пор, пока не будет выполнено второе задание для swaroop.Присвоение swaroop уменьшает количество ссылок для первого экземпляра, вызывая __del__ вызывается потому, что количество ссылок теперь равно нулю.Но поскольку вы имеете в виду Person4 по имени внутри __del__(), когда Предыдущая страница экземпляр исчезает, это уменьшает новое Person4.population граф, вместо старого Person4 подсчет численности населения.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top