setattr, удаление объектов и циклический сбор мусора - PullRequest
3 голосов
/ 22 марта 2012

Я хотел бы понять, как удаление объектов работает на Python.Вот очень простая связка кода.

class A(object):

    def __init__(self):
        setattr(self, "test", self._test)

    def _test(self):
        print "Hello, World!"

    def __del__(self):
        print "I'm dying!"

class B(object):

    def test(self):
        print "Hello, World!"

    def __del__(self):
        print "I'm dying"

print "----------Test on A"
A().test()
print "----------Test on B"
B().test()

Pythonista признает, что я использую версию Python 2.x.В частности, этот код выполняется на установке Python 2.7.1.

Этот код выводит следующее:

----------Test on A
Hello, World!
----------Test on B
Hello, World!
I'm dying

Удивительно, но объект A не удаляется.Я могу понять, почему, поскольку оператор setattr в __init__ создает циклическую ссылку.Но этот вопрос, кажется, легко разрешить.

Наконец, эта страница в документации python (Поддержка циклического сбора мусора) показывает, что возможно иметь дело с такого рода циклическими ссылками.

Я хотел бы знать:

  • , почему я никогда не прохожу свой метод __del__ в классе A?
  • , если мой диагноз о циклической референциихорошо, почему мой object подкласс не поддерживает циклическую сборку мусора?
  • наконец, как бороться с этим видом setattr, если я действительно хочу пройти через __del__?

Примечание. В A, если setattr указывает на другой метод моего модуля, проблем нет.

Ответы [ 2 ]

1 голос
/ 22 марта 2012

Факт 1

Методы экземпляра обычно хранятся в классе .Интерпретатор сначала ищет их в экземпляре __dict__, который завершается ошибкой, а затем просматривает класс, который завершается успешно.

Когда вы динамически устанавливаете метод экземпляра A в __init__, вы создаетессылка на него в словаре экземпляра.Эта ссылка является циклической, поэтому счетчик ссылок никогда не обнулится, и счетчик ссылок не очистит A up.

>>> class A(object):
...     def _test(self): pass
...     def __init__(self):
...             self.test = self._test
... 
>>> a = A()
>>> a.__dict__['test'].im_self

Fact 2

Сборщик мусора - это то, что Python использует дляиметь дело с циркулярными ссылками.К сожалению, он не может обрабатывать объекты с помощью __del__ методов, поскольку в целом он не может определить безопасный порядок их вызова.Вместо этого он просто помещает все такие объекты в gc.garbage.Затем вы можете пойти посмотреть, чтобы разорвать циклы, чтобы они могли быть освобождены.Из документов

gc.garbage

Список объектов, для которых сборщик обнаружил, что они недоступны, но не могут быть освобождены (объекты, которые нельзя собрать).По умолчанию этот список содержит только объекты с __del__() методами.Объекты, которые имеют __del__() методы и являются частью эталонного цикла, приводят к невозможности сбора всего эталонного цикла, включая объекты не обязательно в цикле, но достижимые только из него.Python не собирает такие циклы автоматически, потому что, как правило, Python не может угадать безопасный порядок запуска методов __del__().Если вы знаете безопасный порядок, вы можете форсировать проблему, изучая список мусора и явно прерывая циклы из-за ваших объектов в списке.Обратите внимание, что эти объекты остаются живыми даже благодаря тому, что находятся в списке мусора, поэтому их также следует удалять из мусора.Например, после прерывания цикла выполните del gc.garbage[:], чтобы очистить список.Как правило, лучше избегать этой проблемы, не создавая циклы, содержащие объекты с методами __del__(), и в этом случае можно проверить garbage, чтобы убедиться, что такие циклы не создаются.

Следовательно

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

0 голосов
/ 22 марта 2012

Вам следует внимательно прочитать документацию по методу __del__ - особенно ту часть, в которой объекты с методами __del__ изменяют способ работы коллектора.

В модуле gc есть несколько крючков, которые вы можете почистить самостоятельно.

Я подозреваю, что , просто не имеющий здесь __del__ метода , приведет к правильной очистке вашего объекта. Вы можете проверить это, просмотрев gc.garbage и проверив, присутствует ли ваш экземпляр A.

...