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, не поддерживают слабые ссылки даже при наследовании.
Типы расширений могут легко быть настроены для поддержки слабых ссылок; см. Поддержка слабых ссылок.
-
class weakref.ref(object[, callback]) -
Возвращает слабую ссылку на object. Исходный объект можно получить, вызвав объект ссылки, если ссылаемый объект всё ещё жив; если ссылаемый объект больше не жив, вызов объекта ссылки вернёт
None. Если callback указан и не равенNone, и возвращённый объект weakref всё ещё жив, callback будет вызван, когда объект собирается быть удалён; объект weak reference будет передан в качестве единственного параметра callback; сам ссылаемый объект уже будет недоступен.Разрешается создание множества слабых ссылок на один и тот же объект. Функции обратного вызова, зарегистрированные для каждой слабой ссылки, будут вызываться от последней зарегистрированной функции к старейшей.
Исключения, поднятые функцией обратного вызова, будут отображены на стандартном выходе ошибок, но не могут быть перехвачены; они обрабатываются точно так же, как исключения, поднятые из метода
__del__()объекта.Слабые ссылки являются хешируемыми, если объект object является хешируемым. Они будут сохранять своё значение хэша даже после удаления object. Если
hash()вызывается в первый раз только после удаления object, вызов подниметTypeError.Слабые ссылки поддерживают проверки на равенство, но не порядок. Если ссылаемые объекты всё ещё живы, две ссылки имеют ту же взаимосвязь по равенству, что и их ссылаемые объекты (независимо от callback). Если один из ссылаемых объектов был удалён, ссылки равны только если объекты ссылок являются одним и тем же объектом.
Это тип, допускающий наследование, а не функция-фабрика.
-
__callback__ -
Это атрибут только для чтения, возвращающий текущий callback, связанный со слабой ссылкой. Если callback отсутствует или если ссылаемый объект weakref больше не жив, этот атрибут будет иметь значение
None.
Изменено в версии 3.4: Добавлен атрибут
__callback__. -
-
weakref.proxy(object[, callback]) -
Возвращает прокси к object, использующий слабую ссылку. Это позволяет использовать прокси в большинстве контекстов, вместо явного разыменования, которое требуется для объектов слабых ссылок. Возвращаемый объект будет иметь тип либо
ProxyTypeилиCallableProxyType, в зависимости от того, является ли object вызываемым. Объекты прокси не являются хешируемыми независимо от ссылаемого объекта; это предотвращает ряд проблем, связанных с их фундаментально изменчивой природой, и предотвращает их использование в качестве ключей словарей. callback такой же, как и одноимённый параметр функцииref().
-
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имеет значение 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(ExtendedRef, self).__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(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!") <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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.7/library/weakref.html