V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
nowheremanx
V2EX  ›  Python

Python 的 object 没有如期通过__del__释放资源怎么办?

  •  
  •   nowheremanx · 2023-10-06 12:01:38 +08:00 · 2016 次点击
    这是一个创建于 419 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如有一个 class Task ,创建的时候会生产不少本地临时文件,整个生命周期都会用这个文件。然而 Task 没有用之后,或者程序崩溃的时候,就应该合理地销毁这些临时文件。

    按理说每次 run 函数执行完,task 就没有在 context 里了,应该要被 GC 销毁,销毁的时候顺带执行资源释放。然而实际运行中,临时资源没有被释放,或者马上释放。往往是要等到整个程序结束后才开始释放。很显然,__del__没有马上被执行。

    如果把 main 放到新的子进程里跑,临时文件就更不可控了。 用“destructor”来释放资源的原因是,我无法保证使用者会记得主动去 call 释放的函数。 有没有大神来答疑解惑一下,这种资源释放如何解决。

    网上很多讨论,似乎也没有办法?就算是用 weakref ,也没有解决问题: https://docs.python.org/3.6/library/weakref.html#comparing-finalizers-with-del-methods


    ```
    class Task:
    def __init__(self):
    self._resource = create_local_files()

    def __del__(self):
    self._resource.cleanup()

    def run():
    task = Task()
    ....

    def main():
    while True:
    try:
    run()
    except:
    pass
    time.sleep(60)
    ```
    20 条回复    2023-10-08 12:42:16 +08:00
    t133
        1
    t133  
       2023-10-06 12:30:00 +08:00 via iPhone
    使用 with tempfile 这种试试?
    aijam
        2
    aijam  
       2023-10-06 12:34:08 +08:00
    +1 用 with
    ysc3839
        3
    ysc3839  
       2023-10-06 12:39:09 +08:00 via Android
    gc 语言就是不确定释放时机的,因此不能用 C++的 RAII 这种写法,只能用语言提供的 with 等机制。
    vituralfuture
        4
    vituralfuture  
       2023-10-06 12:42:58 +08:00
    GC 是无法确定时机的,不一定是立刻释放,往往是内存占用达到阈值了或者满足其他条件了才会开始 GC

    参考 https://docs.python.org/3/library/gc.html ,可以看看 python 提供的 GC 接口,可以调试 GC ,也可以立刻调用 GC

    另外还可以把清理资源这些操作放到 run 函数末尾
    BBCCBB
        5
    BBCCBB  
       2023-10-06 12:57:15 +08:00
    使用者必须得保证正确释放, 不然就没法完了. 文件, io 流都是这样的..
    直接用 with/try finally.
    julyclyde
        6
    julyclyde  
       2023-10-06 16:21:43 +08:00
    run 函数末尾试试 del task
    ch2
        7
    ch2  
       2023-10-06 19:04:56 +08:00
    暴露一个 cleanup 函数出去吧
    nowheremanx
        8
    nowheremanx  
    OP
       2023-10-07 06:43:05 +08:00
    @julyclyde del task 也不一定有用,因为 GC 并不会立即释放。而且 python 中各种引用太复杂了,可能无法保证 reference 为 0 。 最令我震惊的是,程序结束之后,有些文件也不会释放。
    nowheremanx
        9
    nowheremanx  
    OP
       2023-10-07 06:44:53 +08:00
    @ch2 是的,只能要求用户手动 cleanup 。 如果没有 cleanup 直接结束的话,扔一个 warning 好了。这是目前想到的方案,类似于 http 客户端或者数据库客户端的 behaviour 。
    dayeye2006199
        10
    dayeye2006199  
       2023-10-07 07:48:18 +08:00 via Android
    弄成一个 contextmanger ,清理逻辑显式写在里面
    nowheremanx
        11
    nowheremanx  
    OP
       2023-10-07 09:02:05 +08:00
    @dayeye2006199 这个是正要加的 feature ,但是我个人不是很喜欢 with ,代码块都要右移,所以最早的时候用__del__帮忙解决资源回收。这才踩坑了
    Maerd
        12
    Maerd  
       2023-10-07 11:38:25 +08:00
    我很好奇为什么你认为让 gc 来“帮”你清除文件是合理的,并且还预想程序崩溃时也能自动清理,这在任何语言中都是不可靠的操作,对于文件操作我们应该手动去处理。
    一般来说,python 中的临时文件都是使用 tempfile 模块来管理,tempfile 会自动在程序退出的时候删除文件
    julyclyde
        13
    julyclyde  
       2023-10-07 12:18:53 +08:00
    @nowheremanx gc 不 *立即* 释放,是很可以理解的。手工 del 可以“提醒”在可做可不做的情况下,让他去做。但至于说引用数没有降到 0 ,那可能是你自己需要解决的问题了

    至于程序结束后还没释放……可能是 bug ??
    julyclyde
        14
    julyclyde  
       2023-10-07 12:19:21 +08:00
    @nowheremanx 哈哈哈代码块右移,好像是 python 被人诟病的问题之一,就是随便加点啥就右移很远了
    julyclyde
        15
    julyclyde  
       2023-10-07 12:21:18 +08:00
    @Maerd tempfile 的自动清理工作也是靠 context manager 的。并不是在程序退出的时候执行
    Maerd
        16
    Maerd  
       2023-10-07 15:05:21 +08:00
    @julyclyde 哦这点是我说错了,tempfile 的清理是 context manager 结束或者关闭临时文件对象后会删除,这样免除了手动删除一次的问题
    julyclyde
        17
    julyclyde  
       2023-10-07 17:31:48 +08:00
    @Maerd 还有在文件关闭时删除的功能吗?怎么实现的?
    bianhui
        18
    bianhui  
       2023-10-08 09:20:05 +08:00
    首先,系统 gc 就不应该要去尝试控制。提供一个 clean 方法或者上下文管理器就行了,别老想着 pua 别的程序员,open 函数也不一定有人全部记得 close 。不会是所有的事情都要拦在自己身上。别人写不好是别人的事,就算是上下文管理器,别人也可以直接通过__enter__去调用。把自己的 api 构建好就行了。
    Maerd
        19
    Maerd  
       2023-10-08 10:30:00 +08:00
    @julyclyde 如果文件对象是 fp ,使用 fp.close()会关闭并自动清理文件
    julyclyde
        20
    julyclyde  
       2023-10-08 12:42:16 +08:00
    @Maerd Under Unix, the directory entry for the file is either not created at all or is removed immediately after the file is created. Other platforms do not support this; your code should not rely on a temporary file created using this function having or not having a visible name in the file system.
    大概是 deleted immediately 的方法。学习了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2794 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 00:24 · PVG 08:24 · LAX 16:24 · JFK 19:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.