2016-04-22 4 views
41

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

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

ответ

33

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

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

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

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

11

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

Как упоминалось питон документация объяснил weakrefhere:

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

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

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

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

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

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

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

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

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

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

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

Вот пример в питона 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> 
+2

Я ничего не вижу в кавычках документов о том, что на самом деле делает '__weakref__' (в нем содержится слабый опорный синглтон). Позаботьтесь об этом? – dhke

+1

@dhke: То же самое здесь ... Понятие слабых ссылок ясно, я действительно задумываюсь о точном значении и функции '__weakref__'. Я подозреваю, что это возможно настолько тривиально, что они просто опустили его ... – Michael

+0

Учитывая ваше редактирование и последнее предложение в сообщении: '__weakref__' - это всего лишь флаг, который гласит:« Да, я могу слабо ссылаться ». (?) – Michael

22

[Edit 1: Объясните, связанный список природы и когда weakrefs повторно используются]

Интересно, что official documentation несколько не-просветительский по этой теме:

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

type object documentation по теме, кажется, не способствовать этому слишком много:

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

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

Пример:

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

>>> import weakref 
>>> class A(object): pass 
>>> 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), но не как цепочка продолжается (b ->c).

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

+0

Хорошо ... так что 'obj .__ weakref__' помнит, что слабая ссылка' wr', указывающая на 'obj' an, будет использоваться для аннулирования' wr', когда 'obj' собирает мусор? – Michael

+0

@Michael Сборщик мусора полностью игнорирует слабые стороны при сборе * достижимых * объектов. Слабый реф просто не считается ссылкой с точки зрения сборщика мусора. Weakrefs рассматриваются только при очистке (https://github.com/python/cpython/blob/a66496cb53fd4e2be7f4304dbb8d170042c4e004/Modules/gcmodule.c#L599). GC пересекает список weakrefs, делает их недействительными и вызывает любой обратный вызов на weakref. – dhke