Могут ли обратные вызовы `weakref` заменять` __del__`?
Есть ли какие-либо препятствия, препятствующие weakref
делать все, что __del__
делает, но с гораздо более сильными гарантиями (например, finalize
гарантирует, что вызов будет выполнен до выхода переводчика, а порядок вызовов будет определен и т.д.)?
Кажется, что в далеком прошлом считалось, что weakref
в конечном итоге приведет к удалению __del__
с языка.
Что помешало этому?
Кажется, что несколько случаев использования для __del__
, и все те, о которых я знаю, похоже, работают как минимум (и обычно намного лучше ) с weakref
обратными вызовами или weakref.finalize
.
Update:
С PEP 442 резко улучшается поведение __del__
, а проблемы с weakref
, упомянутые @gz и @user2357112, Мне интересно, будет ли язык вообще двигаться к тому, чтобы сделать __del__
более надежным или использовать weakref
вместо __del__
или и то, и другое.
Ответы
Ответ 1
Там несколько прагматическая причина __del__
все еще существует. Несколько знаковых улучшений weakref
, включая finalize
, были new в Python 3.4. Итак, заменив __del__
лучшими слабыми файлами, пропустил окно для изменения языка с помощью py3k.
Я думаю, что большинство применений может быть заменено базовыми функциями weakref, но я поражен этим наблюдением Ричарда Оудкерка в вопрос 15528 где предложено и реализовано finalize
:
[Обратные вызовы Weakref] являются низкими уровнями, и разработка правильного их использования требует немного царапин на голове. Нужно найти где-нибудь, чтобы хранить слабые стороны до тех пор, пока референт не будет мертв и не будет случайно сохранить референт в живых. Затем нужно убедиться, что обратный вызов освобождает слабое значение (не оставляя никаких оставшихся ref-циклов).
Если это опция, использование метода __del__
гораздо менее затруднительно.
В любом случае, возможно, вопрос должен быть поднят снова, когда рассматривается Python 4?;)
Ответ 2
Ответ на вопрос действительно зависит от варианта использования, а также предположим, что различные реализации интерпретатора Python не имеют того же самого поведения GC, что и CPython.
PyPy, в частности, не вызывает __del__
как-то, поскольку объект del
-учет или выходит из области видимости, но "через некоторое время". Таким образом, код, который опирается на поведение CPython __del__
, будет разбит на PyPy и другие альтернативные интерпретаторы.
Я бы порекомендовал использовать __enter__
и __exit__
вместе с ключевым словом with
. Например
from __future__ import print_function
class MyClass(object):
def __enter__(self):
print("Entering")
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting")
# 'with MyClass()' can be used but want to show that
# code in exit is executed regardless of object
# life time
obj = MyClass()
with obj:
print("Stuff happening in between")
Результат:
Entering
Stuff happening in between
Exiting
Вышеуказанный порядок утверждений гарантирован и не зависит от поведения GC.
Вещи, которые должны быть выполнены, как только код в блоке with
завершается, могут идти в __exit__
, например, вещи, которые обычно помещаются в деструктор - очистка файлов, освобождение блокировок и т.д.
Последующий del
объект, или он выходит из области видимости, в конечном итоге очистит ссылку на объект, опять же в зависимости от реализации интерпретатора, но все, что должно быть сделано, лучше всего не полагаться на это поведение.