Шаблон делегата Python-как избежать циклической ссылки?


Я хотел бы спросить, приведет ли использование шаблона делегата в Python к циклическим ссылкам, и если да, то как лучше всего реализовать его, чтобы гарантировать, что объект и его делегат будут собраны в мусор?

В задаче с вышеуказанной проблемы можно избежать, используя слабую ссылку на делегат. В C++ мы не вызываем delete для делегата. Я нашел ссылку на слабый ссылочный модуль Python здесь: http://docs.python.org/library/weakref.html . похоже, что правдоподобным подходом может быть создание слабой ссылки для ссылки на переменную экземпляра с помощью этого модуля, но я не уверен.

Поскольку я погуглил этот вопрос и не смог найти на него ответы, мне интересно, является ли это вообще проблемой в Python или есть общее решение (без необходимости в модуле weakref), о котором я не знаю? Кроме того, я искал stackoverflow, прежде чем задать, но вопросы, которые я нашел, либо касаются циклического импорта, либо делегируют шаблон в целом и не специфична для Python и проблема циклических ссылок.

Заранее благодарю за любые ответы.

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

class A(object):
    def __init__(self):
        self.delegate = None

        # Some other instance variables that keep track of state for performing some tasks.

    def doSomething(self):
        if self.delegate is not None:
            self.delegate.doSomething()
        else:
            print('Cannot perform task because delegate is not set.')

    # Other methods not shown.

class B(object):
    def __init__(self):
        self.a = A() # Need to keep object 'a' from garbage collected so as to preserve its state information.
        self.a.delegate = self  # Is this a circular reference? How to 'fix' it so that A and B will eventually be garbage collected?

    def doSomething(self):
        print('B doing something')

    # Other methods not shown.

EDIT:

Прочитав некоторые ответы, я решил уточнить свой вопрос. Я понимаю, что Python имеет сборку мусора. В чем я не был уверен, так это в том, что будет ли он выполнять сборку мусора для объектов с круговыми ссылками. Мои опасения связаны со следующим отрывком из документа Python:

Деталь реализации CPython: CPython в настоящее время использует схема отсчета ссылок с (необязательным) задержанным обнаружением циклически связанный мусор, который собирает большинство объектов, как только они стать недостижимым, но не гарантировано собирать мусор. содержащие циклические ссылки . Смотрите документацию ГК модуль для получения информации по управлению сбором циклического мусора. Другой реализации действуют по-разному, и CPython может измениться. Не зависим о немедленном завершении объектов, когда они становятся недостижимыми (напр.: всегда закрывайте файлы).

Отрывок в его первоначальном виде можно найти здесь: http://docs.python.org/reference/datamodel.html смелая установка-моя.

Следующий пост дает более ясное объяснение проблемы кругового движения. ссылочные объекты и почему это предотвратит сборку мусора на этих объектах (по крайней мере, в обычном режиме): http://www.electricmonk.nl/log/2008/07/07/python-destructor-and-garbage-collection-notes/.

Далее, я только что наткнулся на ответ Алекса Мартелли на следующий вопрос о том, должны ли пользователи Python беспокоиться о циклических ссылках: должен ли я беспокоиться о циклических ссылках в Python? из его ответа я заключаю, что, хотя круговая ссылка объекты в конечном итоге будут собраны в мусор, но будут накладные расходы. Является ли она существенной, зависит от программы.

Далее, он упомянул, чтобы использовать модуль weakref Python, но явно не сказал, как это сделать.

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

Еще раз спасибо за ответы.

2 4

2 ответа:

Python уже выполняет сборку мусора. Вам нужно только сделать что-то особенное, если вы пишете свои собственные типы контейнеров в C, как расширения.

Демо: запустите эту программу и следите за использованием памяти , а не.

class C(object):
    pass

def circular():
    for x in range(10**4):
        for y in range(10**4):
            a = C()
            b = C()
            a.x = b
            b.x = a

circular()

Сноска: следующая функция ничего не делает, удалите ее.

def setDelegate(self, delegate):
    self.delegate = delegate

Вместо вызова x.setDelegate(y) можно использовать x.delegate = y. Вы можете перегружать доступ к элементам в Python, поэтому нет никакой пользы в написании метода.

Почему бы в конце не собрать мусор? Когда сценарий будет завершен и python завершит выполнение, весь раздел памяти будет помечен для сборки мусора и (в конечном итоге) восстановления ОС.

Если вы выполняете это в длительно работающей программе, как только A и B разыменованы, то память будет восстановлена.