ChatGPT解决这个技术问题 Extra ChatGPT

在使用 Python 'with' 语句时捕获异常

我不知道如何处理 python 'with' 语句的异常。如果我有代码:

with open("a.txt") as f:
    print f.readlines()

我真的很想处理“文件未找到异常”以做某事。但我不会写

with open("a.txt") as f:
    print f.readlines()
except:
    print 'oops'

并且不能写

with open("a.txt") as f:
    print f.readlines()
else:
    print 'oops'

在 try/except 语句中包含 with 也不起作用,并且不会引发异常。为了以 Pythonic 方式处理 with 语句中的失败,我该怎么做?

你是什么意思“在try / except语句中包含'with'不起作用:不引发异常”with 语句不会神奇地破坏周围的 try...except 语句。
有趣的是,Java 的 try-with-resources 语句确实 完全支持您想要的这个用例。 docs.oracle.com/javase/tutorial/essential/exceptions/…

D
Douglas Leeder
from __future__ import with_statement

try:
    with open( "a.txt" ) as f :
        print f.readlines()
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
    print 'oops'

如果您想要对公开调用与工作代码中的错误进行不同的处理,您可以执行以下操作:

try:
    f = open('foo.txt')
except IOError:
    print('error')
else:
    with f:
        print f.readlines()

stackoverflow.com/questions/5205811/… 中所述,此处的 try 块实在是太宽泛了。创建上下文管理器时的异常与 with 语句主体中的异常之间没有区别,因此它可能不是所有用例的有效解决方案。
@rbaleksandar如果我没记错的话,我的评论严格指答案中的第一个示例,其中整个 with 语句位于 try/except 块内(因此,即使您有内部 try/expect 块,他们让逃脱的任何异常都会仍然击中外部)。道格拉斯随后添加了第二个示例,以解决这种区别很重要的情况。
在这个例子中文件会被关闭吗?我问是因为它是在“with”范围之外打开的。
@MikeCollins 退出“with”将关闭打开的文件,即使文件在“with”之前打开也是如此。
在第一个示例中,将 OSError 用于 Python 3.3+。如图here:3.3版更改:EnvironmentError、IOError、WindowsError、socket.error、select.error和mmap.error已合并到OSError中,构造函数可能返回子类。
j
jscs

利用 with 语句的最佳“Pythonic”方法被列为 PEP 343 中的示例 #6,它给出了语句的背景。

@contextmanager
def opened_w_error(filename, mode="r"):
    try:
        f = open(filename, mode)
    except IOError, err:
        yield None, err
    else:
        try:
            yield f, None
        finally:
            f.close()

使用如下:

with opened_w_error("/etc/passwd", "a") as (f, err):
    if err:
        print "IOError:", err
    else:
        f.write("guido::0:0::/:/bin/sh\n")

我喜欢它,但感觉有点太黑魔法了。它对读者来说并不完全明确
@PaulSeeb您为什么不定义它并避免每次需要时都这样做?它是在您的应用程序级别定义的,它与任何其他上下文管理器一样神奇。我认为使用 with 语句的人会清楚地理解它(如果您不喜欢它,函数的名称也可能更具表现力)。 “with”语句本身被设计为以这种方式工作,定义一个“安全”代码块并将检查功能委托给上下文管理器(使代码更清晰)。
所有这些麻烦只是因为没有在用户代码中编写 finally 块。我开始认为我们都因 with 声明中的长期炒作症状而受苦。
在python中处理异常的最好方法是编写一个捕获并返回它们的函数?严重地?处理异常的 Pythonic 方式是使用 try...except 语句。
这太棒了,谢谢!这是唯一不违反 try 语句中包含多于 1 行的规则的答案,并且还允许我保留 with 语句而不是单独打开和关闭连接。
R
Russia Must Remove Putin

在使用 Python 'with' 语句时捕获异常

with 语句在没有 __future__ 导入 since Python 2.6 的情况下可用。您可以将其作为 early as Python 2.5 (但此时是升级的时候了!):

from __future__ import with_statement

这是您所拥有的最接近纠正的东西。您快到了,但 with 没有 except 子句:

with open("a.txt") as f: print(f.readlines()) except: # <- with 没有 except 子句。打印('哎呀')

上下文管理器的 __exit__ 方法,如果它返回 False,它将在完成时重新引发错误。如果它返回 True,它将抑制它。内置 open__exit__ 不返回 True,因此您只需将它嵌套在 try 中,除了块:

try:
    with open("a.txt") as f:
        print(f.readlines())
except Exception as error: 
    print('oops')

和标准样板:不要使用捕获 BaseException 和所有其他可能的异常和警告的裸 except:。至少与 Exception 一样具体,对于这个错误,也许可以捕获 IOError。只捕获您准备处理的错误。

所以在这种情况下,你会这样做:

>>> try:
...     with open("a.txt") as f:
...         print(f.readlines())
... except IOError as error: 
...     print('oops')
... 
oops

a
a_guest

区分从复合 with 语句引发的异常的可能来源

区分 with 语句中发生的异常是很棘手的,因为它们可能起源于不同的地方。可以从以下任一位置(或其中调用的函数)引发异常:

上下文管理器.__init__

上下文管理器.__enter__

的身体

上下文管理器.__exit__

有关更多详细信息,请参阅有关 Context Manager Types 的文档。

如果我们想区分这些不同的情况,仅仅将 with 包装成 try .. except 是不够的。考虑以下示例(使用 ValueError 作为示例,但当然它可以替换为任何其他异常类型):

try:
    with ContextManager():
        BLOCK
except ValueError as err:
    print(err)

这里 except 将捕获源自所有四个不同位置的异常,因此不允许区分它们。如果我们将上下文管理器对象的实例移到 with 之外,我们可以区分 __init__BLOCK / __enter__ / __exit__

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        with mgr:
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        # At this point we still cannot distinguish between exceptions raised from
        # __enter__, BLOCK, __exit__ (also BLOCK since we didn't catch ValueError in the body)
        pass

实际上,这只是对 __init__ 部分有所帮助,但我们可以添加一个额外的哨兵变量来检查 with 的主体是否开始执行(即区分 __enter__ 和其他部分):

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        entered_body = False
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        else:
            # At this point we know the exception came either from BLOCK or from __exit__
            pass

棘手的部分是区分源自 BLOCK__exit__ 的异常,因为逃脱 with 主体的异常将被传递给 __exit__,它可以决定如何处理它(请参阅 the docs)。但是,如果 __exit__ 引发自身,则原始异常将被新异常替换。为了处理这些情况,我们可以在 with 的主体中添加一个通用 except 子句来存储任何可能会在不被注意的情况下逃脱的潜在异常,并将其与稍后在最外面的 except 中捕获的异常进行比较 - 如果它们是相同的,这意味着原点是 BLOCK,否则它是 __exit__(如果 __exit__ 通过返回一个真值来抑制异常,那么最外面的 except 将不会被执行)。

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    entered_body = exc_escaped_from_body = False
    try:
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
            except Exception as err:  # this exception would normally escape without notice
                # we store this exception to check in the outer `except` clause
                # whether it is the same (otherwise it comes from __exit__)
                exc_escaped_from_body = err
                raise  # re-raise since we didn't intend to handle it, just needed to store it
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        elif err is exc_escaped_from_body:
            print('BLOCK raised:', err)
        else:
            print('__exit__ raised:', err)

使用 PEP 343 中提到的等效形式的替代方法

PEP 343 -- The "with" Statement 指定 with 语句的等效“non-with”版本。在这里,我们可以很容易地用 try ... except 包装各个部分,从而区分不同的潜在错误源:

import sys

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        value = type(mgr).__enter__(mgr)
    except ValueError as err:
        print('__enter__ raised:', err)
    else:
        exit = type(mgr).__exit__
        exc = True
        try:
            try:
                BLOCK
            except TypeError:
                pass
            except:
                exc = False
                try:
                    exit_val = exit(mgr, *sys.exc_info())
                except ValueError as err:
                    print('__exit__ raised:', err)
                else:
                    if not exit_val:
                        raise
        except ValueError as err:
            print('BLOCK raised:', err)
        finally:
            if exc:
                try:
                    exit(mgr, None, None, None)
                except ValueError as err:
                    print('__exit__ raised:', err)

通常更简单的方法就可以了

这种特殊异常处理的需求应该很少见,通常将整个 with 包装在 try ... except 块中就足够了。特别是如果各种错误源由不同的(自定义)异常类型指示(需要相应地设计上下文管理器),我们可以轻松区分它们。例如:

try:
    with ContextManager():
        BLOCK
except InitError:  # raised from __init__
    ...
except AcquireResourceError:  # raised from __enter__
    ...
except ValueError:  # raised from BLOCK
    ...
except ReleaseResourceError:  # raised from __exit__
    ...

G
General Grievance

我在我制作的程序中使用了它:

try:
    with open(os.path.join(basedir, "rules.txt")) as f:
        self.rules.setText(f.read())
except FileNotFoundError:
    self.rules.setText("Sorry, unable to read rules")

self.rules.setText("抱歉,无法读取规则") 换行。所以没有表现出来。
除了部分也是如此