tjm*_*tjm 20 python exception with-statement python-3.x
我知道我可以打开多个文件,比如
with open('a', 'rb') as a, open('b', 'rb') as b:
Run Code Online (Sandbox Code Playgroud)
但我有一个情况,我有一个文件列表打开,我想知道当文件数量提前未知时,首选方法是做什么的.就像是,
with [ open(f, 'rb') for f in files ] as fs:
Run Code Online (Sandbox Code Playgroud)
(但是AttributeError因为列表没有实现而失败了__exit__)
我不介意使用像
try:
fs = [ open(f, 'rb') for f in files ]
....
finally:
for f in fs:
f.close()
Run Code Online (Sandbox Code Playgroud)
但是我不确定如果在尝试打开它们时抛出一些文件会发生什么.是否会fs在块中正确定义管理打开的文件finally?
Dun*_*can 13
不,fs除非所有open()呼叫都成功完成,否则您的代码不会初始化.这应该工作:
fs = []
try:
for f in files:
fs.append(open(f, 'rb'))
....
finally:
for f in fs:
f.close()
Run Code Online (Sandbox Code Playgroud)
另请注意,f.close()可能会失败,因此您可能希望捕获并忽略(或以其他方式处理)任何失败.
当然,为什么不呢,这是一个应该做的配方.创建一个可以输入任意数量的上下文的上下文管理器"池"(通过调用它的enter()方法),它们将在套件结束时清理.
class ContextPool(object):
def __init__(self):
self._pool = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_tb):
for close in reversed(self._pool):
close(exc_type, exc_value, exc_tb)
def enter(self, context):
close = context.__exit__
result = context.__enter__()
self._pool.append(close)
return result
Run Code Online (Sandbox Code Playgroud)
例如:
>>> class StubContextManager(object):
... def __init__(self, name):
... self.__name = name
... def __repr__(self):
... return "%s(%r)" % (type(self).__name__, self.__name)
...
... def __enter__(self):
... print "called %r.__enter__()" % (self)
...
... def __exit__(self, *args):
... print "called %r.__exit__%r" % (self, args)
...
>>> with ContextPool() as pool:
... pool.enter(StubContextManager("foo"))
... pool.enter(StubContextManager("bar"))
... 1/0
...
called StubContextManager('foo').__enter__()
called StubContextManager('bar').__enter__()
called StubContextManager('bar').__exit__(<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x02958648>)
called StubContextManager('foo').__exit__(<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x02958648>)
Traceback (most recent call last):
File "<pyshell#67>", line 4, in <module>
1/0
ZeroDivisionError: integer division or modulo by zero
>>>
Run Code Online (Sandbox Code Playgroud)
注意事项:上下文管理器不应该在他们的__exit__()方法中引发异常,但如果他们这样做,这个方法不会对所有上下文管理器进行清理.类似地,即使每个上下文管理器都指示应该忽略异常(通过True从其退出方法返回),这仍然会允许引发异常.
ExitStack模块中的类contextlib提供您正在寻找的功能。文档中提到的规范用例是管理动态数量的文件。
with ExitStack() as stack:
files = [stack.enter_context(open(fname)) for fname in filenames]
# All opened files will automatically be closed at the end of
# the with statement, even if attempts to open files later
# in the list raise an exception
Run Code Online (Sandbox Code Playgroud)