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.
-
-
class weakref.ReferenceType -
Объект типа для объектов слабых ссылок.
-
class weakref.ProxyType -
Объект типа для прокси объектов, которые не являются вызываемыми.
-
class 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 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/weakref.html