Могут ли обратные вызовы `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 объект, или он выходит из области видимости, в конечном итоге очистит ссылку на объект, опять же в зависимости от реализации интерпретатора, но все, что должно быть сделано, лучше всего не полагаться на это поведение.