【问题标题】:Can `weakref` callbacks replace `__del__`?`weakref` 回调可以替换 `__del__` 吗?
【发布时间】:2017-10-01 05:12:06
【问题描述】:

是否有任何障碍阻止weakref__del__ 所做的所有事情,但有更强有力的保证(例如,finalize 保证调用将在解释器退出之前进行,并且调用顺序很好-定义等)?

似乎在遥远的过去 it was thought weakref 最终会导致 __del__ 从语言中删除。

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

似乎有few use cases for __del__,而且我所知道的所有这些似乎至少与weakref 回调或weakref.finalize 一样好(通常更好)。

更新:

随着PEP 442 显着改善__del__ 的行为,以及@gz 和@user2357112 提到的对weakref 的担忧,我想知道该语言是否普遍朝着使__del__ 更可靠的方向发展,或者倾向于使用weakref 而不是__del__,或两者兼而有之。

【问题讨论】:

  • “是什么阻止了这种情况的发生?” 这是一个很少可能能够回答的问题。我建议您也将其发布到python-list,希望能引起一些核心开发人员的注意。
  • Python 的weakref 支持非常糟糕,因此您不能将weakrefs 用于许多您想要weakref 的对象类型。这对于您自己编写的类型通常无关紧要,但如果您的类型类似于 tuple 子类,则不能对其进行弱引用。
  • 另外,weakref 回调无法访问引用对象,因此需要更仔细的设计。
  • @user2357112 例如,根据文档,weakrefs 不能用于生成器。这是否意味着目前绝对没有办法用weakrefs替换生成器__del__?或者有一些解决方法(例如创建一个单独的对象,该对象仅与将用作weakref.finalize 触发器的生成器链接)?

标签: python python-3.x garbage-collection destructor weak-references


【解决方案1】:

__del__ 仍然存在是出于某种务实的原因。几个显着的weakref 改进,包括finalize,是new in Python 3.4。因此,用更好的weakrefs 替换__del__ 错过了使用py3k 进行语言破坏性更改的窗口。

我认为大多数用途可以被基本的weakref功能取代,但我对Richard Oudkerk在issue 15528中提出并实施finalize的观察感到震惊:

[Weakref 回调] 是低级的,要弄清楚如何正确使用它们需要一点头绪。必须找到某个地方来存储弱引用,直到所指对象死亡,并且不会意外地保持所指对象存活。然后必须确保回调释放弱引用(不留下任何剩余的引用循环)。

当它是一个选项时,使用__del__ 方法的麻烦要少得多。

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

【讨论】:

    【解决方案2】:

    这个问题的答案确实取决于用例,并且还建议您考虑不同的 Python 解释器实现与 CPython 的 GC 行为不同。

    PyPy 特别是 调用 __del__ as-soon-因为对象是 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 或超出范围,最终将清除对象引用,这再次取决于解释器的实现,但最好立即完成的事情不依赖于该行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-17
      • 2013-09-05
      • 1970-01-01
      • 1970-01-01
      • 2013-02-03
      • 1970-01-01
      • 2011-07-17
      • 1970-01-01
      相关资源
      最近更新 更多