<p>本打算发表评论,但事情变得太复杂了,不能就此置之不理,但我确实有答案。在</p>
<p>代码的修正版本可以简化为</p>
<pre><code>@contextmanager
def myopen(path):
try:
yield open(path)
except Exception as e:
print(e.__traceback__)
</code></pre>
<p>在尝试之前,让我们使用以下命令获取打开文件句柄的计数:</p>
^{pr2}$
<p>现在使用我们的上下文管理器</p>
<pre><code>>>> with myopen('/tmp/a_file') as f:
... print(f.read())
... print(os.listdir('/proc/self/fd'))
...
Contents of file
['0', '1', '2', '3', '4']
</code></pre>
<p>是的,文件描述符计数增加了,但是现在我们已经脱离了上下文管理器,让我们看看</p>
<pre><code>>>> print(os.listdir('/proc/self/fd'))
['0', '1', '2', '3', '4']
</code></pre>
<p>这就破坏了为文件设置上下文管理器的目的(我们希望使用默认的自动关闭功能,所以重新启动解释器,然后尝试这样做。在</p>
<pre><code>@contextmanager
def myopen(path):
try:
with open(path) as f:
yield f
except Exception as e:
print(e.__traceback__)
</code></pre>
<p>重新运行我们的测试</p>
<pre><code>>>> with myopen('/tmp/a_file') as f:
... print(f.read())
... print(os.listdir('/proc/self/fd'))
...
Contents of file
['0', '1', '2', '3', '4']
</code></pre>
<p>现在在上下文管理器之外</p>
<pre><code>>>> print(os.listdir('/proc/self/fd'))
['0', '1', '2', '3']
</code></pre>
<p>是的,看起来它工作了(文件已成功关闭),但是如果有一个不存在的路径来查看异常处理呢?在</p>
<pre><code>>>> with myopen('/tmp/no_path') as f:
... print(f.read())
...
<traceback object at 0x7f6b64b618c8>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.4/contextlib.py", line 61, in __enter__
raise RuntimeError("generator didn't yield") from None
RuntimeError: generator didn't yield
</code></pre>
<p>异常块确实触发了,请注意<code>traceback</code>对象,但是由于上下文管理器没有正确生成,因此会引发一个与您之前看到的不同的异常。我不知道该推荐什么,但我推荐的是记录错误(使用记录器),然后重新读取异常。您可以考虑返回某种类型的伪对象,该对象在读取时会引发异常,或者什么也不返回,但是您需要确定什么是最适合您的情况的。在</p>