当用作上下文管理器时,NpzFile 是否会自行关闭?

Is NpzFile closing itself when used as a context manager?

numpy.load() 的文档字符串中,我发现了以下警告:

For .npz files, the returned instance of NpzFile class must be closed to avoid leaking file descriptors.

我注意到,返回的 NpzFile 对象同时具有 __enter__()__exit__() 方法。

如果我这样使用它会自动关闭它吗:

>>> with numpy.load('my_mile.npz') as data:
...     A = data['A']

?

是的。使用 with 语句将关闭类文件对象。这是一个例子,直接来自 the documentation:

with load('foo.npz') as data:
    a = data['a']

简答

是的,它会在上下文结束后自动关闭文件对象,因为 NpzFile 对象同时具有 __enter__()__exit__() 方法(参见 here)。


已接受答案的更正

更正已接受的答案(以及 documentation):

应用上下文管理器的正确方法不是with load('foo.npz') as data而是with numpy.load('foo.npz') as data。使用前者会导致预期的名称错误:NameError: name 'load' is not defined.


长答案

在关闭 with expression as var 上下文管理器的范围后,var 仍然作为上下文管理器之外的对象保留。然而,numpy.load()的情况下,文件描述符var在作用域外是不可访问的上下文管理器。 考虑以下示例:

# Creating a dictionary of data to be saved using numpy.savez
data_dict = {'some_string': 'Whosebug',
'some_integer': 10000,
'some_array': numpy.array([0,1,2,3,4])
}

# Saving the data
numpy.savez(file='./data_dict.npz', **data_dict)

# Loading the 'data_dict' using context manager
with numpy.load('data_dict.npz') as dt:
    string_ = dt['some_string']
    integer_ = dt['some_integer']
    array_ = dt['some_array']
# OR
with numpy.load('data_dict.npz') as dt:
    dt_ = dict(dt) # if you want the entire dictionary to be loaded as is

如果您现在尝试访问上下文管理器之外的文件描述符,它将简单地 return NpzFile 对象,其内存地址如下:

>>> dt
Out[]: <numpy.lib.npyio.NpzFile at 0x7ffba63bb7c0>

但是,正如所料,您将无法访问它的任何属性。例如,当您执行以下操作时,您会得到 AttributeError

>>> dt['some_string']
Out[]: Traceback (most recent call last):
.
.
File ".../site-packages/numpy/lib/npyio.py", line 249, in __getitem__
    bytes = self.zip.open(key)
AttributeError: 'NoneType' object has no attribute 'open'

这是因为,在 with 上下文管理器结束后,NpzFile 对象的 self.zip 变量被分配 None 值(参见第一个 URL 中的 def close(self):上面,在 dunder __exit__())

中被调用

注意 1: dt.keys() returns(如预期)一个 KeysView 对象并执行 list(dt.keys()) 给你一个键列表' dt 的姓名:['some_string', 'some_integer', 'some_array']。但是,仍然无法访问存储在这些键上(在上下文管理器范围内)的值(在上下文管理器范围之外)。

注意 2: 我特意使用了包含 non-numpy-array values 的字典只是为了表明可以存储使用 numpy.savez() 的此类词典。但是,这不是存储此类数据的推荐方法。