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__ определены для данного типа, поддержка слабых ссылок отключена, если строка '__weakref__' также присутствует в последовательности строк в объявлении __slots__. Подробности см. в документации __slots__.
-
class weakref.ref(object[, callback]) -
Возвращает слабую ссылку на object. Исходный объект может быть получен, вызвав объект ссылки, если ссылкуемый объект все еще жив; если ссылкуемый объект больше не жив, вызов объекта ссылки приведет к возвращению
None. Если callback задан и не равенNone, и возвращённый объект weakref все ещё жив, callback будет вызван, когда объект будет готов к финализации; объект слабой ссылки будет передан в качестве единственного параметра в callback; ссылкуемый объект больше недоступен.Разрешается создание многих слабых ссылок на один и тот же объект. Функции обратного вызова, зарегистрированные для каждой слабой ссылки, будут вызваны от последней зарегистрированной функции обратного вызова к самой старой.
Исключения, возбужденные функцией обратного вызова, будут выведены на стандартный поток ошибок, но не могут быть перехвачены; они обрабатываются точно так же, как исключения, возбуждаемые методом
__del__()объекта.Слабые ссылки являются хешируемыми, если object хешируем. Они сохранят своё значение хеша даже после удаления object. Если функция
hash()будет вызвана в первый раз только после удаления object, вызов возбудитTypeError.Слабые ссылки поддерживают проверки на равенство, но не порядок. Если ссылкуемые объекты всё ещё живы, два ссылка имеют такие же отношения равенства, как и их ссылки на объекты (независимо от callback). Если любой ссылкуемый объект был удален, ссылки равны только в том случае, если объекты ссылок — один и тот же объект.
Это подклассируемый тип, а не функция-фабрика.
-
__callback__ -
Это только для чтения атрибут возвращает функцию обратного вызова, которая в данный момент связана со слабой ссылкой. Если нет функции обратного вызова или если объект, на который указывает слабая ссылка, больше не жив, этот атрибут будет иметь значение
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
Определенный таким образом, наш финализатор получает ссылку только на необходимые данные для соответствующей очистки каталога. Если объект никогда не попадает в сборку мусора, финализатор по-прежнему будет вызван при выходе.
Другое преимущество финализаторов, основанных на слабых ссылках, заключается в том, что они могут использоваться для регистрации финализаторов для классов, определение которых контролируется третьей стороной, например, для запуска кода при разгрузке модуля:
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