`weakref`回调能否替换`__del__`方法?

20

是否有任何障碍阻止weakref做所有__del__可以做的事情,但提供更强的保证(例如,finalize保证在解释器退出之前将进行调用,并且调用顺序是明确定义的等)?

似乎在遥远的过去曾经认为weakref最终会导致从语言中删除__del__

是什么阻止了这种情况发生?

似乎__del__用例很少, 我所知道的所有用例都可以使用weakref回调或weakref.finalize至少同样好(通常更好)地工作。

更新:

随着PEP 442的出现,__del__的行为得到了显著改进,同时也有@ gz和@ user2357112提出了对weakref的担忧。我想知道语言是否普遍朝着使__del__更加可靠的方向发展,还是朝着使用weakref而不是__del__的方向发展,或者两者都有。


1
“这是什么原因导致了这种情况?”这是一个很少有人能够回答的问题。我建议你也在 python-list 上发布这个问题,希望能引起一些核心开发者的注意。 - Dimitris Fasarakis Hilliard
2
Python 的 weakref 支持相当差,因此您无法对许多希望弱引用的对象类型进行弱引用。这通常对自己编写的类型没有影响,但如果您的类型类似于 tuple 的子类,您将无法对其进行弱引用。 - user2357112
1
此外,弱引用回调函数无法访问引用对象,因此需要更加谨慎的设计。 - user2357112
例如,根据文档,弱引用不能应用于生成器。这是否意味着目前绝对无法使用弱引用替换生成器的 __del__ 方法?或者有一些解决方法(例如创建一个仅与生成器相关联的单独对象,该对象将作为 weakref.finalize 的触发器)? - max
2个回答

4

有一个相当实用的原因,__del__仍然存在。包括finalize在内的几个重要的weakref改进是Python 3.4中新推出的。因此,用更好的弱引用替换__del__错过了py3k语言破坏性变化的窗口。

我认为大多数用途可以通过基本的弱引用功能来替换,但我被Richard Oudkerk在问题15528中提出并实现的finalize的观察所震撼:

[弱引用回调]是低级别的,需要一些头脑操练才能正确使用它们。必须找到一个地方来存储弱引用,直到引用已死亡,而不会意外保持引用的生命。然后,必须确保回调释放弱引用(而没有留下任何残留的引用循环)。

如果有选择的话,使用__del__方法要少得多。

无论如何,或许应该在考虑 Python 4 的时候再次提出这个问题? ;)

1
答案取决于使用情况,并建议您考虑不同的Python解释器实现与CPython的GC行为不同。特别是PyPy并不会在对象被删除或超出范围时立即调用__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操作或使其超出作用域,最终将清除对象引用,这也取决于解释器的实现,但应立即执行的操作最好不要依赖这种行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接