Подтвердить что ты не робот

Что такое __weakref__ в Python?

Удивительно, нет явной документации для __weakref__. Ниже описаны слабые ссылки . __weakref__ также упоминается в документации __slots__. Но я ничего не мог найти о __weakref__.

Что такое __weakref__? - Это просто член, действующий как флаг: если присутствует, объект может быть слабосвязан? - Или это функция/переменная, которая может быть переопределена/назначена для получения желаемого поведения? Как?

4b9b3361

Ответ 1

__weakref__ - это просто непрозрачный объект, который ссылается на все слабые ссылки на текущий объект. На самом деле это экземпляр weakref (или иногда weakproxy), который является и слабой ссылкой на объект и часть двусвязного списка ко всем слабым ссылкам для этого объекта.

Это просто деталь реализации, которая позволяет сборщику мусора сообщать о слабых ссылках, которые он собрал, и больше не разрешать доступ к нему, лежащему в основе указателя.

Слабая ссылка не может полагаться на проверку ссылочного счета объекта, на который он ссылается. Это связано с тем, что эта память, возможно, была восстановлена ​​и теперь используется другим объектом. Наилучший сценарий, с которым столкнется виртуальная машина, в худшем случае слабая ссылка позволит получить доступ к объекту, на который он первоначально не ссылался. Вот почему сборщик мусора должен сообщить о слабой ссылке, которую он ссылается, уже недействителен.

См. weakrefobject.h для структуры и C-API для этого объекта. И деталь реализации здесь

Ответ 2

[Редактировать 1: Объясните природу связанного списка и повторите использование слабых ссылок]

Интересно, что официальная документация несколько не просвещает по этой теме:

Без переменной __weakref__ для каждого экземпляра классы, определяющие __slots__, не поддерживают слабые ссылки на свои экземпляры. Если необходима слабая опорная поддержка, добавьте __weakref__ в последовательность строк в объявлении __slots__.

type документация по объекту по этой теме, похоже, не слишком помогает:

Если объявление типов __slots__ содержит слот с именем __weakref__, этот слот становится слабой головкой списка ссылок для экземпляров типа, а смещение слотов сохраняется в типах tp_weaklistoffset.

Слабые ссылки образуют связанный список. Глава этого списка (первая слабая ссылка на объект) доступна через __weakref__. Weakrefs повторно используются по возможности, поэтому список (а не список Python!) Обычно либо пуст, либо содержит один элемент.

Пример:

Когда вы впервые используете weakref.ref(), вы создаете новую слабую цепочку ссылок для целевого объекта. Головкой этой цепочки является новый weakref и сохраняется в целевом объекте __weakref__:

>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b)
>>> print(b is c is a.__weakref__)
True

Как мы видим, b повторно используется. Мы можем заставить python создать новый weakref, например. добавление параметра обратного вызова:

>>> def callback():
>>>   pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b, callback)
>>> print(b is c is a.__weakref__)
False

Теперь b is a.__weakref__, а c - вторая ссылка в цепочке. Цепочка ссылок напрямую не доступна из кода Python. Мы видим только головной элемент цепи (b), но не как цепочка продолжается (bc).

Итак, __weakref__ является главой внутреннего связанного списка всех слабых ссылок на объект. Я не могу найти какую-либо официальную документацию, где эта роль __weakref__ кратко объясняется, поэтому, вероятно, не следует полагаться на это поведение, поскольку это деталь реализации.

Ответ 3

переменная __weakref__ - это атрибут, который позволяет объекту поддерживать слабые ссылки и сохранять слабые ссылки на объект.

Как вы упомянули, документация python объяснила weakref здесь:

когда единственными остальными ссылками на референт являются слабые ссылки, сбор мусора может уничтожить референт и повторно использовать его память для чего-то еще.

Таким образом, обязанность слабых ссылок обеспечивает условия для объекта, чтобы иметь возможность собирать мусор независимо от его типа и объема.

И о __slots__ документация объясняет их очень хорошо:

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

Значение по умолчанию может быть переопределено путем определения __slots__ в определении класса. Объявление __slots__ принимает последовательность переменных экземпляра и резервирует достаточно места в каждом экземпляре для хранения значения для каждой переменной. Пространство сохраняется, поскольку __dict__ не создается для каждого экземпляра.

Итак, поскольку с помощью __slots__ вы будете управлять требуемым хранилищем для своего атрибута, это фактически предотвращает автоматическое создание __dict__ и __weakref__ для каждого экземпляра. Которая __weakref__ является необходимой переменной каждого объекта, чтобы иметь возможность справляться со слабыми ссылками.

Как говорится в документации для класса object.__slots__:

Эта переменная класса может быть назначена строка, итерируемая или последовательность строк с именами переменных, используемыми экземплярами. __slots__ резервирует место для объявленных переменных и предотвращает автоматическое создание __dict__ и __weakref__ для каждого экземпляра.

В двух словах рассказывается, что __slots__ предназначены для управления распределением хранилища вручную, а поскольку __weakref__ - это лицензия на принятие слабых ссылок для объектов, связанных с хранением (потому что способности собирать мусор), поэтому __slots__ будет управлять __weakref__, а также управлять атрибутом __dict__.

Также документация показала вам способ сделать объект для поддержки слабых ссылок вдоль стороны использования __slots__:

Без переменной __weakref__ для каждого экземпляра классы, определяющие __slots__, не поддерживают слабые ссылки на свои экземпляры. Если необходима слабая опорная поддержка, добавьте '__weakref__' в последовательность строк в объявлении __slots__.

Вот пример в python 3.X:

>>> class Test:
...     __slots__ = ['a', 'b']
... 
>>> 
>>> import weakref
>>> 
>>> t = Test()
>>> 
>>> r = weakref.ref(t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create weak reference to 'Test' object
>>> 
>>> class Test:
...     __slots__ = ['a', 'b', '__weakref__']
... 
>>> t = Test()
>>> r = weakref.ref(t)
>>> 
>>> t.__weakref__
<weakref at 0x7f735bc55d68; to 'Test' at 0x7f735bc51fc8>

Но в python 2.7 там, хотя документация похожа на вышеупомянутые документы, создавая слабую ссылку из экземпляров, которые не предоставляют переменную __weakref__ в своих именах __slots__, не поднимают TypeError:

>>> class Test:
...    __slots__ = ['a', 'b']
... 
>>> t = Test()
>>> 
>>> r = weakref.ref(t)
>>> 
>>> r
<weakref at 0x7fe49f4185d0; to 'instance' at 0x7fe4a3e75f80>