weakref — Слабые ссылки

Исходный код: Lib/weakref.py


Модуль weakref позволяет программисту Python создавать слабые ссылки к объектам.

Далее термин референт означает объект, на который ссылается слабая ссылка.

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

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

Например, при наличии большого количества объектов двоичного изображения может потребоваться связать имя с каждым из них. Если используемый Python словарь для сопоставления имен с изображениями или изображений с именами, объекты изображения останутся живыми только потому, что они появляются в словарях в виде значения или ключей. Классы WeakKeyDictionary и WeakValueDictionary, поставляемые модулем weakref, являются альтернативой, использующей слабые ссылки для построения сопоставлений, которые не поддерживают живость объектов только потому, что они появляются в объектах сопоставления. Если, например, объект изображения является значеним в WeakValueDictionary, то когда последние оставшиеся ссылки на этот объект изображения являются слабыми ссылками, удерживаемыми слабыми отображениями, сборка мусора может восстановить объект, и его соответствующие записи в слабых отображениях просто удаляются.

WeakKeyDictionary и WeakValueDictionary используют слабые ссылки в своей реализации, настраивая колбэк функции для слабых ссылок, которые уведомляют слабые словари, когда ключ или значение были восстановлены при сборе мусора. WeakSet реализует интерфейс set, но сохраняет слабые ссылки на его элементы, как это делает WeakKeyDictionary.

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

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

Не все объекты могут иметь слабые ссылки; те объекты, которые могут включать класс сущности, функции, написанные в Python (но не в C), методы сущности, множества, замороженные множества, некоторые файловые объекты, генераторы, type объекты, сокеты, множества, deques, объекты образца регулярного выражения и кодовый объекты.

Изменено в версии 3.2: Добавлена поддержка пthread.lock, threading.Lock и кодовые объекты.

Несколько встроенных типов, таких как list и dict, не поддерживают слабые ссылки напрямую, но могут добавлять поддержку через подкласс:

class Dict(dict):
    pass

obj = Dict(red=1, green=2, blue=3)   # этот объект является слабой ссылкой

Детали реализации CPython: Другие встроенные типы, такие как tuple и int, не поддерживаются слабые ссылки, даже если они разделены на подклассы.

Типы расширений могут быть легко сделаны для поддержки слабых ссылок; см. Слабая справочная поддержка.

class weakref.ref(object[, callback])

Возвращает слабую ссылку на object. Исходный объект можно получить, вызвав ссылочный объект, если референт еще жив; если референт больше не активен, вызов объекта ссылки приведет к возвращенный None. Если callback будет обеспечен и не None, и объект возвращенный weakref все еще жив, то колбэк назовут, когда объект соберется, завершены; слабый опорный объект будет передан в качестве единственного параметра в колбэк; референт больше не будет доступен.

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

Исключения, создаваемые колбэк, будут отмечаться в стандартных выходных данных ошибок, но не могут распространяться; они обрабатываются точно так же, как исключения из метода __del__() объекта.

Слабые ссылки хэшируемыми, если объект является хэшируемым. Они сохранят свои хеш- значение даже после удаления объекта. Если hash() вызывается в первый раз только после удаления объекта, вызов вызывает TypeError.

Слабые ссылки поддерживают тесты на равенство, но не порядок. Если референты все еще живы, две ссылки имеют те же отношения равенства, что и их референты (независимо от callback). Если любой из ссылочных объектов удален, ссылки равны только в том случае, если ссылочные объекты являются одним и тем же объектом.

Это тип подкласса, а не заводская функция.

__callback__

Этот атрибут только для чтения возвращает колбэк, в настоящее время связанный с слабой ссылкой. Если колбэк нет или если референта слабака уже нет в живых, то этот атрибут будет иметь значение None.

Изменено в версии 3.4: Добавлен __callback__ атрибут.

weakref.proxy(object[, callback])

Возвращает прокси для object, использующего слабую ссылку. Поддерживает использование прокси в большинстве контекстов вместо того, чтобы требовать явного используемый отмены ссылки со слабыми объектами ссылки. Возвращенный объект будет иметь тип ProxyType или CallableProxyType в зависимости от того, является ли object вызываемым. Прокси-объекты не хэшируемыми независимо от референта; это позволяет избежать ряда проблем, связанных с их фундаментально изменяемым характером, и предотвратить их использование в качестве словарных ключей. callback совпадает с параметром с именем функции ref().

Изменено в версии 3.8: Расширение поддержки оператора на прокси-объектах для включения операторов умножения матриц @ и @=.

weakref.getweakrefcount(object)

Возвращает количество слабых ссылок и прокси, ссылающихся на object.

weakref.getweakrefs(object)

Возвращает список всех слабых ссылочных и прокси-объектов, ссылающихся на object.

class weakref.WeakKeyDictionary([dict])

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

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

WeakKeyDictionary.keyrefs()

Возвращает итератор слабых ссылок на ключи.

class weakref.WeakValueDictionary([dict])

Класс сопоставления, который ссылается на слабые значения. Элементы в словаре будут отброшены, если больше нет сильной ссылки на значение.

WeakValueDictionary объекты имеют дополнительный метод, имеющий те же проблемы, что и keyrefs() метод WeakKeyDictionary объектов.

WeakValueDictionary.valuerefs()

Возвращает итератор слабых ссылок на значения.

class weakref.WeakSet([elements])

Задать класс, сохраняющий слабые ссылки на его элементы. Элемент будет отброшен, если на него больше нет сильной ссылки.

class weakref.WeakMethod(method)

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

>>> class C:
...     def method(self):
...         print("method called!")
...
>>> c = C()
>>> r = weakref.ref(c.method)
>>> r()
>>> r = weakref.WeakMethod(c.method)
>>> r()
<bound method C.method of <__main__.C object at 0x7fc859830220>>
>>> r()()
method called!
>>> del c
>>> gc.collect()
0
>>> r()
>>>

Добавлено в версии 3.4.

class weakref.finalize(obj, func, *args, **kwargs)

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

Финализатор считается живым до тех пор, пока он не будет вызван (явно или при сборе мусора), а после этого мертв. Вызов живого финализатора возвращает результатом оценки func(*arg, **kwargs), тогда как вызов мертвого финализатора возвращает None.

Исключения, создаваемые колбэки финализатора во время сбора мусора, будут отображаться на стандартных выходных данных ошибок, но не могут быть распространены. Они обрабатываются так же, как исключения, возникающие из метода __del__() объекта или колбэк слабой ссылки.

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

Финализатор никогда не будет вызывать свои колбэк в течение более поздней части выключение интерпретатора, когда глобальные модули могут быть заменены None.

__call__()

Если self жив, отметьте его как мертвого и возвращает результат вызова func(*args, **kwargs). Если self мертв, тогда возвращает None.

detach()

Если self жив, отметьте его как мертвого и возвращает кортеж (obj, func, args, kwargs). Если self мертв, тогда возвращает None.

peek()

Если self жив, возвращает кортеж (obj, func, args, kwargs). Если self мертв, тогда возвращает None.

alive

Свойство true, если финализатор жив, в противном случае false.

atexit

Записываемое логическое свойство, которое по умолчанию имеет значение true. Когда программа завершает работу, она вызывает все оставшиеся живые финализаторы, для которых atexit соответствует действительности. Они называются в обратном порядке создания.

Примечание

Важно обеспечить, чтобы func, args и kwargs не владели какими-либо ссылками на obj, прямо или косвенно, поскольку в противном случае obj никогда не будет собираться мусор. В частности, func не должно быть связанным методом obj.

Добавлено в версии 3.4.

weakref.ReferenceType

Объект-тип для объектов со слабыми ссылками.

weakref.ProxyType

Объект типа для прокси объектов, которые не являются вызываемыми.

weakref.CallableProxyType

Объект типа для прокси вызываемых объектов.

weakref.ProxyTypes

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

См.также

PEP 205 - слабые ссылки
Предложение и обоснование этой функции, включая ссылки на более ранние реализации и информацию об аналогичных функциях на других языках.

Слабые ссылочные объекты

Слабые ссылочные объекты не имеют методов и атрибутов, кроме ref.__callback__. Слабый ссылочный объект позволяет получить ссылочный объект, если он все еще существует, путем вызова:

>>> import weakref
>>> class Object:
...     pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True

Если ссылочный объект больше не существует, вызов ссылочного объекта возвращает None:

>>> del o, o2
>>> print(r())
None

Проверка наличия слабого ссылочного объекта должна выполняться с помощью выражения ref() is not None. Обычно код приложения, которым требуется использовать ссылочный объект, должны следовать этому образцу:

# r - слабый ссылочный объект
o = r()
if o is None:
    # ссылка была собрана сборщиком мусора
    print("Object has been deallocated; can't frobnicate.")
else:
    print("Object is still live!")
    o.do_something_useful()

Использование отдельного теста на «живость» создает условия гонки в поточных приложениях; другой поток может привести к тому, что слабая ссылка станет недействительной до вызова слабой ссылки; показанный выше идиом безопасен в многопоточных приложениях, а также в однопоточных приложениях.

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

В этом примере показано, как можно используемый подкласс ref для хранения дополнительной информации об объекте и воздействия на значение, возвращенный при обращении к ссылочному объекту:

import weakref

class ExtendedRef(weakref.ref):
    def __init__(self, ob, callback=None, /, **annotations):
        super(ExtendedRef, self).__init__(ob, callback)
        self.__counter = 0
        for k, v in annotations.items():
            setattr(self, k, v)

    def __call__(self):
        """Возвращает пару, содержащую ссылку и количество вызовов ссылки.
        """
        ob = super(ExtendedRef, self).__call__()
        if ob is not None:
            self.__counter += 1
            ob = (ob, self.__counter)
        return ob

Пример

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

import weakref

_id2obj_dict = weakref.WeakValueDictionary()

def remember(obj):
    oid = id(obj)
    _id2obj_dict[oid] = obj
    return oid

def id2obj(oid):
    return _id2obj_dict[oid]

Объекты финализатора

Основное преимущество использования finalize заключается в простоте регистрации колбэк без необходимости сохранения объекта возвращенного финализатора. Для сущности

>>> import weakref
>>> class Object:
...     pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")  #doctest:+ELLIPSIS
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!

Финализатор также можно вызвать напрямую. Однако финализатор вызовет колбэк не более одного раза.

>>> def callback(x, y, z):
...     print("CALLBACK")
...     return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f()                     # колбэк не вызван, потому что финализатор мертв
>>> del obj                 # колбэк не вызван, потому что финализатор мертв

Можно отменить регистрацию финализатора с помощью метода detach(). Это приводит к уничтожению завершителя и возвращает аргументов, переданных конструктору при его создании.

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()                                           #doctest:+ELLIPSIS
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK

Если для параметра atexit атрибут не задано значение False, то при завершении программы будет вызван финализатор, если она все еще жива. Для сущности

>>> obj = Object()
>>> weakref.finalize(obj, print, "obj dead or exiting")
<finalize object at ...; for 'Object' at ...>
>>> exit()
obj dead or exiting

Сравнение финализаторов с __del__() методами

Предположим, мы хотим создать класс, сущности которого представляют временные каталоги. Каталоги должны быть удалены вместе с их содержимым, когда произойдет первое из следующих событий:

  • объект собирается мусором,
  • вызывается метод remove() объекта или
  • программа завершается.

Мы можем попытаться реализовать класс с помощью метода __del__() следующим образом:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()

    def remove(self):
        if self.name is not None:
            shutil.rmtree(self.name)
            self.name = None

    @property
    def removed(self):
        return self.name is None

    def __del__(self):
        self.remove()

Начиная с Python 3.4, __del__() методы больше не предотвращают сбор мусора эталонными циклами, и глобальные модули больше не вынуждены None во время выключение интерпретатора. Так что этот код должен работать без каких-либо проблем на CPython.

Однако обработка __del__() способов, как известно, специфична для реализации, поскольку она зависит от внутренних деталей реализации сборщика мусора интерпретатора.

Более надежной альтернативой может быть определение финализатора, который ссылается только на конкретные функции и объекты, которые ему необходимы, вместо того, чтобы иметь доступ к полному состояние объекта:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()
        self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

    def remove(self):
        self._finalizer()

    @property
    def removed(self):
        return not self._finalizer.alive

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

Другое преимущество финализаторов на основе слабых ссылок состоит в том, что они могут быть используемы для регистрации финализаторов для классов, в которых определение контролируется третьей стороной, например, при выполнении кода при разгрузке модуля:

import weakref, sys
def unloading_module():
    # неявная ссылка на глобальные объекты модуля из тела функции
weakref.finalize(sys.modules[__name__], unloading_module)

Примечание

При создании объекта финализатора в демоническом потоке по мере завершения программы существует вероятность того, что финализатор не будет вызван при выходе. Однако в демоническом потоке atexit.register() try: ... finally: ... и with: ... не гарантируют, что очистка также произойдет.