删除字典会关闭字典中的文件描述符吗?

Does deleting a dictionary close the file descriptors inside the dict?

澄清

确实有两个问题。已更新以使其更清楚。

我有:

t = {
    'fd': open("filename", 'r')
}

我了解 del t['fd'] 删除密钥并关闭文件。对吗?

是否 del t 对包含的对象调用 del(在本例中为 fd)?

没有。您必须关闭打开的文件(见注释)。

或者,但它可能不适合您的项目,以更安全的方式在上下文管理器中打开它。

with open("filename", 'r') as fd:
    ...

如果您同时打开未知数量的文件,您可能需要编写自己的上下文管理器或更简单地使用 contextlib.ExitStack

注: 从 : "3.10.0 文档 / Python教程/ 7. 输入输出 / 7.2.读写文件 https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files": 警告调用 f.write() 而不使用 with 关键字或调用 f.close() 可能会导致 f.write() 的参数未被完全写入到磁盘,即使程序成功退出。

你的问题没有你想象的那么微不足道。但归根结底,这一切都归结为理解什么是指针以及垃圾收集器的工作原理。

确实,通过 t['fd'],您可以删除字典中的该条目。您所做的是删除指向该条目的指针。如果你有如下字典:

t = { 'a':3, 'b':4 }

然后当您执行 del t 时,您删除了指向字典 t 的指针。由于因此没有进一步引用字典键,垃圾收集器也会删除它们,从而释放所有字典内存。

但是,只要您有文件描述符,就可以将其删除,但这样做并不可取,因为文件描述符(fd、指向文件的指针等)是一种方式程序员与文件交互,如果描述符被突然删除,文件的状态可能会因处于不一致状态而被破坏。

因此,最好在停止处理文件之前调用 close 函数。此功能会为您处理所有这些事情。

您想做的事情的行为是不确定的。根据 Python(CPython、PyPi、...)的实现和内部功能,这可能有效或无效:

工作示例:

t = {
    'fd': open('data.txt', 'r')
}

def hook_close_fd():
    print('del dictionary, close the file')

t['fd'].close = hook_close_fd

del t

输出:

del dictionary, close the file

在这种情况下,close 函数在删除时被调用

无效示例:

t = {
    'fd': open('data.txt', 'r')
}

def hook_close_fd():
    print('del dictionary, close the file')

t['fd'].close = hook_close_fd

3 / 0

第一个 运行 的输出:

---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-211-2d81419599a9> in <module>
     10 t['fd'].close = hook_close_fd
     11 
---> 12 3 / 0

ZeroDivisionError: division by zero

第二个运行的输出:

del dictionary, close the file
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-212-2d81419599a9> in <module>
     10 t['fd'].close = hook_close_fd
     11 
---> 12 3 / 0

ZeroDivisionError: division by zero

如您所见,当引发异常时,您无法确定文件描述符是否会正确关闭(尤其是如果您自己没有捕获异常)。

是,删除变量 t 关闭文件 (using psutil to show open files per process):

import psutil


def find_open_files():
    for proc in psutil.process_iter():
        if proc.name() == 'python3.9':
            print(proc.open_files())


filename = '/tmp/test.txt'
t = {'fd': open(filename, 'r')}
find_open_files()
del t
find_open_files()

输出:

[popenfile(path='/private/tmp/test.txt', fd=3)]
[]

你的问题的两个部分有完全不同的答案。

  • 删除变量关闭文件不靠谱;有时有效,有时无效,或者有效但以令人惊讶的方式。有时它可能会丢失数据。它肯定无法以有用的方式报告文件错误。

    关闭文件的正确方法是 (a) 使用 with 语句,或 (b) 使用 .close() 方法。

  • 删除对象确实会删除所有包含的对象,但有几点需要注意:

    • 如果这些对象(中的一些)也在另一个变量中,它们将继续存在,直到另一个变量也被删除;

    • 如果这些对象相互引用,它们可能会在之后继续存在一段时间,然后被删除;和

    • 对于不可变对象(字符串、整数),Python 可能会决定保留它们作为优化,但这大部分对我们来说是不可见的,并且在任何情况下都会有所不同版本之间。