python上下文管理中finally块中的Return

2024-09-30 22:14:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近在Python的with语句中遇到了一个奇怪的行为。我有一段代码,它使用Python的上下文管理器来回滚__exit__方法中的配置更改。管理器在__exit__中的finally块中有一个return False值。我在下面的代码中隔离了这种情况—唯一的区别是return语句的缩进:

class Manager1(object):

    def release(self):
        pass # Implementation not important

    def rollback(self):
        # Rollback fails throwing an exception:
        raise Exception("A failure")

    def __enter__(self):
        print "ENTER1"

    def __exit__(self, exc_type, exc_val, exc_tb):
        print "EXIT1"
        try:
            self.rollback()
        finally:
            self.release()
            return False          # The only difference here!


class Manager2(object):

    def release(self):
        pass # Implementation not important

    def rollback(self):
        # Rollback fails throwing an exception:
        raise Exception("A failure")

    def __enter__(self):
        print "ENTER2"

    def __exit__(self, exc_type, exc_val, exc_tb):
        print "EXIT2"
        try:
            self.rollback()
        finally:
            self.release()
        return False      # The only difference here!

在上面的代码中,回滚失败,出现异常。我的问题是,为什么Manager1的行为与Manager2不同。异常不会在Manager1中的with语句之外引发,以及在Manager2中退出时引发异常的原因。在

^{pr2}$

根据documentation of ^{}

If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.

在我看来,在这两种情况下,退出都不返回True,因此在这两种情况下都不应取消该异常。但在Manager1中却是如此。有人能解释一下吗?在

我使用python2.7.6。在


Tags: 代码selffalsereleasereturndefexitexception
2条回答

我认为理解这一点的一个好方法是看一个独立于所有上下文管理器的单独示例:

>>> def test ():
        try:
            print('Before raise')
            raise Exception()
            print('After raise')
        finally:
            print('In finally')
        print('Outside of try/finally')

>>> test()
Before raise
In finally
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    test()
  File "<pyshell#6>", line 4, in test
    raise Exception()
Exception

因此您可以看到,当异常在try块中抛出时,之前的任何代码都将被执行,finally块中的任何代码都将被执行。除此之外,其他的都被跳过了。这是因为抛出的异常结束了函数调用。但是由于异常是在try块中引发的,因此相应的finally块有最后一次运行的机会。在

现在,如果您注释掉函数中的raise行,您将看到所有代码都已执行,因为函数不会过早结束。在

如果finally子句被激活,这意味着try块已经成功完成,或者它引发了一个已被处理的错误,或者try块执行了return。在

在Manager1中,执行return语句作为finally子句的一部分,使其正常终止,返回False。在Manager2类中,finally子句仍在执行,但如果它是由于引发异常而执行的,则它不会阻止异常在调用链上传播,直到被捕获(或直到它通过回溯终止程序)。在

Manager2.__exit__()只有在没有引发异常的情况下才会返回False。在

相关问题 更多 >