我最近写了一个方法,它返回一系列打开的文件;换句话说,类似于:
# this is very much simplified, of course
# the actual code returns file-like objects, not necessarily files
def _iterdir(self, *path):
dr = os.path.join(*path)
paths = imap(lambda fn: os.path.join(dr, fn), os.listdir(dr))
return imap(open, paths)
从语法上讲,如果我执行如下操作,则不需要关闭生成的对象:
for f in _iterdir('/', 'usr'):
make_unicorns_from(f)
# ! f.close()
因此,我决定在上下文管理器中包装
_iterdir
def iterdir(self, *path):
it = self._iterdir(*path)
while 1:
with it.next() as f:
yield f
这似乎工作正常。
我感兴趣的是这样做是否是一种良好的实践。我会在这个模式下遇到任何问题吗(如果抛出异常的话)?
最佳答案
我看到两个问题。一个是,如果一次尝试使用多个文件,则会出现以下情况:
list(iterdir('/', 'usr')) # Doesn't work; they're all closed.
第二种情况不太可能发生在cpython中,但是如果您有一个引用循环,或者您的代码曾经在不同的Python实现上运行,那么问题可能会显现出来。
如果在
make_unicorns_from(f)
中发生异常:for f in iterdir('/', 'usr'):
make_unicorns_from(f) # Uh oh, not enough biomass.
在垃圾收集生成器之前,不会关闭正在使用的文件。此时,将调用生成器的
close
方法,在最后一个GeneratorExit
处引发一个yield
异常,该异常将导致上下文管理器关闭文件。对于cpython的引用计数,这通常是立即发生的。但是,在非引用计数的实现上或存在引用循环的情况下,在运行检测GC传递的循环之前,可能不会收集生成器。这可能需要一段时间。
我的直觉告诉我不要把文件关闭给来电者。你可以的
for f in _iterdir('/', 'usr'):
with f:
make_unicorns_from(f)
即使在生成器中没有
with
也会立即关闭它们,即使抛出异常也是如此。我不知道这是否比让生成器负责关闭文件更好。关于python - 从上下文管理器中 yield 是一种好的做法吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24691234/