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 больше не существует, значением этого атрибута будет
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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/weakref.html