如何确保像通常(但不正确)预期的那样在Python类上调用_del__)函数?

如何确保像通常(但不正确)预期的那样在Python类上调用_del__)函数?,python,python-3.x,python-2.7,Python,Python 3.x,Python 2.7,我理解Python类的\uu del\uu函数:作为析构函数 我也知道有更多的“pythonic”和更优雅的方式来整理,特别是在使用 然而,当编写可能被不太熟悉python方式的用户使用的代码时,当清理非常重要时,有没有一种优雅的方法可以让\uu del_uu作为析构函数可靠地工作,而不影响python对\uu del_u的自然使用 期望\uu del\uu充当析构函数似乎并不不合理,同时也是很常见的。因此,我只是想知道是否有一种优雅的方式可以让它按照预期工作——忽略了关于它是多么pythoni

我理解Python类的
\uu del\uu
函数:作为析构函数

我也知道有更多的“pythonic”和更优雅的方式来整理,特别是在使用

然而,当编写可能被不太熟悉python方式的用户使用的代码时,当清理非常重要时,有没有一种优雅的方法可以让
\uu del_uu
作为析构函数可靠地工作,而不影响python对
\uu del_u
的自然使用


期望
\uu del\uu
充当析构函数似乎并不不合理,同时也是很常见的。因此,我只是想知道是否有一种优雅的方式可以让它按照预期工作——忽略了关于它是多么pythonic的优点的许多争论。

这是我一直采用的一种模式,它通过python模块实现了这一点

class Demo(object):
    def __init__(self, *args, **kwargs):
        import atexit
        atexit.register(self.__del__)

    def __del__(self):
        print("__del__ being called!")

t1 = Demo()
t2 = Demo()

quit()
粘贴到python命令提示符时,这是总输出:

Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> class Demo(object):
...     def __init__(self, *args, **kwargs):
...         import atexit
...         atexit.register(self.__del__)
...
...     def __del__(self):
...         print("__del__ being called!")
...
>>> t1 = Demo()
>>> t2 = Demo()
>>>
>>> quit()
__del__ being called!
__del__ being called!

如果你了解所有这些,为什么不以蟒蛇的方式来做呢?比较另一个清理很重要的类:
tempfile.TemporaryDirectory

with TemporaryDirectory() as tmp:
    # ...
# tmp is deleted

def foo():
    tmp = TemporaryDirectory()
foo()
# tmp is deleted
他们是怎么做到的?以下是相关信息:

import weakref
class Foo():
    def __init__(self, name):
        self.name = name
        self._finalizer = weakref.finalize(self, self._cleanup, self.name)
        print("%s reporting for duty!" % name)

    @classmethod
    def _cleanup(cls, name):
        print("%s feels forgotten! Bye!" % name)

    def cleanup(self):
        if self._finalizer.detach():
            print("%s told to go away! Bye!" % self.name)

def foo():
    print("Calling Arnold")
    tmpfoo = Foo("Arnold")
    print("Finishing with Arnold")

foo()
# => Calling Arnold
# => Arnold reporting for duty
# => Finishing with Arnold
# => Arnold feels forgotten. Bye!

def bar():
    print("Calling Rocky")
    tmpbar = Foo("Rocky")
    tmpbar.cleanup()
    print("Finishing with Rocky")

bar()
# => Calling Rocky
# => Rocky reporting for duty!
# => Rocky told to go away! Bye!
# => Finishing with Rocky
weakref.finalize
将在垃圾收集对象时触发
\u cleanup
,如果对象仍然存在,则在程序结束时触发。我们可以保留finalizer,这样我们就可以显式地终止对象(使用
分离
),并将其标记为dead,这样就不会调用finalizer(当我们想要手动处理清理时)


如果您想用
支持上下文使用,只需添加
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法即可(如上所述,手动清理).

考虑警告用户,如果您必须在
\uuu del\uuu
中进行清理,那么他们的程序实际上已损坏……您希望何时进行清理?范围是什么?记录您的代码,并展示如何将
一起使用的示例。这并不难学,但我确实需要检查这样做对自然垃圾收集的影响,以及如果在退出之前对类进行垃圾收集会发生什么!呸!您可以在
\uuu del\uuu
的末尾取消注册(self.\uu del\uuu)
,以避免垃圾收集+程序结束多次调用它。如果该模块是长时间运行的进程的一部分,该怎么办?您将
\u cleanu
作为类方法的具体原因是什么?@Aran Fey:确实::“注意:重要的是确保
func
args
kwargs
不直接或间接拥有对
obj
的任何引用,否则将永远不会对
obj
进行垃圾收集。”。特别是,
func
不应该是
obj
的绑定方法。“这意味着您没有访问终结器中实例的权限?那太没用了,不是吗?有没有办法将实例传递给终结器?@Aran Fey:这就不符合要点了。通过args/kwargs传递与终结器相关的任何信息,如
name
此处。我认为如果它是无用的,它就不会被使用(很明显,在引用的
TemporaryDirectory
类中就是这样)。@Aran Fey:“没有理由认为这是必要的。”编写Python文档的人肯定认为它是关键的。一方面,如果你有一个对对象的活动引用,它就不能被垃圾收集,所以weakref finalizer永远不会被触发;这是一个很好的理由,为什么它是必要的。尽管如此,在链接页面的底部仍然有一个关于
finalize
vs
\uuu del\uuu
的大讨论,您可能会发现,如果您确实需要从对象中获得一些在注册最终用户时不知道的信息,那么您可能会冒险走
\uu del\uuu
atexit
路线。