为什么我们在Python中需要“ finally”子句?


306

我不知道为什么我们需要finallytry...except...finally报表。我认为,此代码块

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

使用finally以下命令与此相同:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

我想念什么吗?

Answers:


422

如果您提早返回,会有所不同:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

比较一下:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

可能导致差异的其他情况:

  • 如果在except块内引发异常。
  • 如果引发异常,run_code1()但不是TypeError
  • 其他控制流语句,例如continuebreak语句。

1
尝试:#x = Hello + 20 x = 10 + 20,除了:打印'我在else块中'x = 20 + 30其他:打印'我在else块中'x + = 1最后:打印'最后x =% s'%(x)
阿比吉特·萨胡

89

您可以finally用来确保文件或资源被关闭或释放,而不管是否发生异常,即使您没有捕获到该异常也是如此。(或者,如果您没有捕获到该特定异常。)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

在此示例中,使用该with语句会更好,但是这种结构可以用于其他类型的资源。

几年后,我写了一篇博客文章,讲述滥用finally读者可能会觉得有趣的事情。


23

它们不相等。不管其他什么情况发生,最终代码都会运行。这对于必须运行的清理代码很有用。


15
Finally code is run no matter what else happens...除非存在无限循环。或断电。或者os._exit()。还是……
马克·拜尔斯

3
@Mark实际上,sys.exit会引发一个正常的异常。但是,是的,任何导致进程立即终止的事物都意味着没有其他执行。
锑2012年

1
@锑:谢谢。更改为os._exit
Mark Byers

只是想知道,为什么不能输入清除代码,除非只有在发现异常的情况下才会输入清除代码?
斯蒂芬·雅各布

2
@Stephen一方面,即使您从try块返回,代码也最终会运行。在这种情况下,您将不会点击except子句。

18

除了上面的其他答案外,该finally子句无论执行什么都将else执行,而该子句仅在未引发异常的情况下才执行。

例如,无例外地写入文件将输出以下内容:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

输出:

Writing to file.
Write successful.
File closed.

如果有异常,代码将输出以下内容(请注意,故意使文件保持只读状态会导致错误。

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

输出:

Could not write to file.
File closed.

我们可以看到,该finally子句无论有无异常都会执行。希望这可以帮助。


2
即使您不使用“ finally”子句也无法解决问题,因为OP希望知道两者之间的区别,所以这还是可行的,一个很好的例子会引起与IOError不同的错误,以表明在将异常传播到调用者之前,将执行finally子句块。
Reda Drissi

2
我不知道else是一回事。有用的知道。
mazunki

8

代码块不是等效的。finally如果run_code1()引发除以外的异常TypeError,或者run_code2()引发异常,则该子句也将运行,而other_code()在这些情况下,在第一个版本中则不会运行该子句。


7

在您的第一个示例中,如果run_code1()引发一个不是的异常会发生什么TypeError?... other_code()将不会被执行。

将其与finally:版本进行比较:other_code()保证无论是否引发任何异常都将被执行。


7

文档中所述,该finally子句旨在定义在所有情况下都必须执行的清理操作。

如果finally存在,则指定“清理”处理程序。该try 子句被执行,包括any exceptelse子句。如果在任何子句中发生异常并且未进行处理,则将临时保存该异常。该finally子句被执行。如果存在已保存的异常,则会在finally 子句末重新引发。

一个例子:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

如您所见,该finally子句在任何情况下都会执行。在TypeError通过将两个字符串凸起不被处理except子句和后因此再次加注finally条款已经被执行。

在实际的应用程序中,无论是否成功使用资源,finally子句对于释放外部资源(例如文件或网络连接)都是有用的。


4

完美的例子如下:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)

3

finally用于定义“清理动作”finally在离开try语句之前的任何事件中,无论是否发生异常(即使您不处理它),都将执行该子句。

我第二个@Byers的例子。


2

当您要在运行主要工作的代码之前运行“可选”代码并且可选代码可能由于各种原因而失败时,也可以使用Final。

在下面的示例中,我们不确切知道store_some_debug_info可能会引发哪种异常。

我们可以运行:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

但是,大多数的短毛猫都会抱怨捉摸不清一个例外。另外,由于我们选择仅pass针对错误,因此该except块并没有真正增加价值。

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

上面的代码与第一个代码块具有相同的效果,但更为简洁。


2

几年来专业地使用delphi教会了我维护最终使用的清理例程。Delphi几乎会强制使用finally来清理在try块之前创建的所有资源,以免引起内存泄漏。这也是Java,Python和Ruby的工作方式。

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

不管您在尝试和最终之间进行什么操作,资源都会被清理。另外,如果执行从未到达该try块,则不会清除它。(即create_resource本身引发异常),这使您的代码“异常安全”。

至于为什么您实际上需要一个finally块,并不是所有语言都需要。在C ++中,您自动调用了析构函数,这些析构函数在异常展开堆栈时强制执行清除。与尝试...最终的语言相比,我认为这是朝着更清洁的代码方向发展的一步。

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.

2

一个try块只有一个强制性子句:try语句。else,else和finally子句是可选的,并且基于用户首选项。

最终:在Python离开try语句之前,它将在任何情况下在finally块中运行代码,即使它正在结束程序。例如,如果Python在except或else块中运行代码时遇到错误,则在停止程序之前,finally块仍将执行。


1
错了 例外声明是强制性的。– Lucas Azevedo 2月1日,12:04 这是错误的,因为我刚刚编译并运行了带有try-finally块且没有“ except”子句的Python 3.5程序。
罗布·陶

2
我本人尝试过,但我不敢相信,except子句不是强制性的。
captainblack

1

运行以下Python3代码以最终了解最终需求:

情况1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

案例2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

每次尝试以下输入:

  1. 随机整数
  2. 正确的代码是586(尝试此操作,您将得到答案)
  3. 随机字符串

**在学习Python的初期。


1

我试图在要阅读Excel工作表的地方运行代码。问题是,如果有一个没有工作表的文件说:SheetSum我无法将其移动到错误位置!我写的代码是:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

给出错误:

[WinError 32]该进程无法访问文件,因为该文件正在被另一个进程使用

我必须添加完整的try except with finally块并告诉finally我在任何情况下都需要关闭文件:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

否则,文件仍然保持打开状态。

如果finally存在,则指定清除处理程序。该try 子句被执行,包括any exceptelse子句。如果在任何子句中发生异常并且未对其进行处理,则将临时保存异常。该finally子句被执行。如果存在已保存的异常,则会在finally 子句末重新引发。如果finally子句引发另一个异常,则将保存的异常设置为新异常的上下文。

..更多这里

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.