Spec-Zone.ru › Python 3.13

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

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

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

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

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

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

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

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

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

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

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

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

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

class Dict(dict):
    pass

obj = Dict(red=1, green=2, blue=3)   # this object is weak referenceable

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

Типы расширений можно легко сделать поддерживающими слабые ссылки; см. Поддержка слабых ссылок.

Если для заданного типа определён __slots__, поддержка слабых ссылок отключается, если в последовательности строк объявления __slots__ также нет строки '__weakref__'. Подробности см. в документации по __slots__.

class weakref.ref(object[, callback])

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

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

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

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

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

Это тип, допускающий создание подклассов, а не фабричная функция.

__callback__

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

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

weakref.proxy(object[, callback])

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

Обращение к атрибуту объекта-прокси после сборки мусора для объекта ссылки вызовет исключение ReferenceError.

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

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

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

class weakref.WeakKeyDictionary([dict])

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

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

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> d[k2] = 2   # d = {k1: 2}
>>> del k1      # d = {}

Обойти это можно, удалив ключ перед повторным присваиванием:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> del d[k1]
>>> d[k2] = 2   # d = {k2: 2}
>>> del k1      # d = {k2: 2}

Изменено в версии 3.9: Добавлена поддержка операторов | и |= согласно PEP 584.

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

WeakKeyDictionary.keyrefs()

Возвращает итерируемый объект со слабыми ссылками на ключи.

class weakref.WeakValueDictionary([dict])

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

Изменено в версии 3.9: Добавлена поддержка операторов | и |= согласно PEP 584.

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

WeakValueDictionary.valuerefs()

Возвращает итерируемый объект со слабыми ссылками на значения.

class weakref.WeakSet([elements])

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

class weakref.WeakMethod(method[, callback])

Пользовательский подкласс 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()
>>>

callback — это параметр с тем же именем функции ref().

Добавлено в версии 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 имеет значение true. Они вызываются в обратном порядке создания.

Примечание

Важно убедиться, что 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 is a weak reference object
o = r()
if o is None:
    # referent has been garbage collected
    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().__init__(ob, callback)
        self.__counter = 0
        for k, v in annotations.items():
            setattr(self, k, v)

    def __call__(self):
        """Return a pair containing the referent and the number of
        times the reference has been called.
        """
        ob = super().__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!")
<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()                     # callback not called because finalizer dead
>>> del obj                 # callback not called because finalizer dead

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

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()
(<...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

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

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

import weakref, sys
def unloading_module():
    # implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)

Примечание

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

© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/weakref.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API