Python Try和Except语句——如何在Python中处理异常

2021-09-26 17:12:14 浏览数 (9668)

使用 Python 编码时,即使是在语法和逻辑上正确的程序中,您也经常会出现运行时错误。这些错误可能是由无效输入或某些前后矛盾引起的。在 很多语言中都提供了异常处理机制,在Python 中,您可以使用try和except块来更优雅地将大多数这些错误作为异常处理。在本教程中,您将学习的一般语法try和except。然后我们将继续编写简单的示例,讨论可能出错的地方,并使用try和except块提供纠正措施。

Python try 和 except 块的语法

让我们从了解Python 中try和except语句的语法开始介绍。通用模板如下图所示:

try:
	# There can be errors in this block
    
except <error type>:
	# Do this to handle exception;
	# executed if the try block throws an error
    
else:
	# Do this if try block executes successfully without errors
   
finally:
	# This block is always executed

让我们看看不同块的用途:

  • 该try代码块是你想尝试执行的语句块。但是,由于异常可能会出现运行时错误,所以此块可能无法按预期工作。
  • 该except代码块是触发try块时需要执行的语句。它包含一组语句,这些语句通常会为您提供有关try块内出错的一些处理方法。
  • 您应该始终提及您打算在代码块内作为异常捕获的错误类型,由上述代码段中的占位符表示。例如:except<error type>
  • 您也可以不在except指定<error type>. 但是,这不是推荐的做法,因为您没有考虑可能发生的不同类型的错误。
在尝试执行try块内的代码时,也有可能发生多个错误。

例如,您可能正在使用超出范围的索引访问列表,使用错误的字典键,并尝试打开一个不存在的文件 - 所有这些都应该放在try块内。

在这种情况下,你可能会碰到​IndexError​,​KeyError​和​FileNotFoundError​。并且您必须添加与except您预期的错误数量一样多的每种类型的错误一个。

  • 仅当try块在没有错误的情况下执行时,才会触发else块。当您希望在try块成功后执行后续操作时,这可能非常有用。例如,如果您尝试成功打开一个文件,则可能需要读取其内容。

  • 该finally块总是被执行,而不管其他块中发生了什么。当您想在执行特定代码块后释放资源时,这很有用。
注意:else和finally块是可选的。在大多数情况下,您可以只使用try块来尝试做某事,并将错误捕获为except块内的异常。

在接下来的几分钟内,您将使用迄今为止学到的知识来处理 Python 中的异常。让我们开始吧。

如何处理Python中的ZeroDivision(除零异常)错误

考虑下面所示的函数divide()。它接受两个参数 num和div ,并返回除法运算的商num/div。

def divide(num,div):
  return num/div

▶ 使用不同的参数调用函数会按预期返回结果:

res = divide(100,8)
print(res)

# Output
12.5

res = divide(568,64)
print(res)

# Output
8.875

此代码工作正常,直到您尝试除以零:

divide(27,0)

您会看到程序崩溃并抛出ZeroDivisionError:

# Output
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-19-932ea024ce43> in <module>()
----> 1 divide(27,0)

<ipython-input-1-c98670fd7a12> in divide(num, div)
      1 def divide(num,div):
----> 2   return num/div

ZeroDivisionError: division by zero

您可以通过执行以下操作将此除以零作为异常处理:

  • 在try块中,调用divide()函数。从本质上讲,您正在试图将num除以div。
  • 当div为0的情况作为except块内的异常处理。
  • 在此示例中,您可以通过打印一条消息来通知用户他们尝试除以零,从而排除​ZeroDivisionError​异常。

这显示在下面的代码片段中:

try:
    res = divide(num,div)
    print(res)
except ZeroDivisionError:
    print("You tried to divide by zero :( ")

使用有效的输入,代码仍然可以正常工作。

divide(10,2)
# Output
5.0

当您尝试除零时,您会收到发生异常的通知,并且程序会正常结束。

divide(10,0)
# Output
You tried to divide by zero :(

如何在 Python 中处理 TypeError(类型异常)

在本节中,您将看到如何在 Python 中使用try和except处理一个 TypeError。

▶ 考虑以下函数add_10(),它接受一个数字作为参数,将其加 10,然后返回加法的结果。

def add_10(num):
  return num + 10

您可以使用任意数字参数调用该函数,它会正常工作,如下所示:

result = add_10(89)
print(result)

#Output
99

现在尝试add_10()使用"five"而不是调用5。

add_10("five")

您会注意到您的程序崩溃并显示以下错误消息:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-9844e949c84e> in <module>()
----> 1 add_10("five")

<ipython-input-13-2e506d74d919> in add_10(num)
      1 def add_10(num):
----> 2   return num + 10

TypeError: can only concatenate str (not "int") to str

该错误消息​TypeError: can only concatenate str (not "int") to str​说明您只能连接两个字符串,而不能向字符串添加整数。

现在,您有以下内容:

  • 给定一个数字my_num,尝试使用my_num作为参数调用函数add_10()。如果参数是有效类型,则不存在异常
  • 否则,将触发与TypeError对应的except块,通知用户参数的类型无效。

这一点解释如下:

my_num = "five"
try:
  result = add_10(my_num)
  print(result)
except TypeError:
  print("The argument `num` should be a number")

由于您现在已​将TypeError​作为异常处理,因此您只会被告知参数的类型无效。

The argument `num` should be a number

如何在 Python 中处理 IndexError(无效索引)

如果您之前使用过 Python 列表或任何可迭代的 Python数据类型,您可能会遇到​IndexError​.

这是因为通常很难跟踪可迭代对象的所有更改。并且您可能正在尝试访问无效索引处的项。

▶ 在这个例子中,列表my_list有 4 个项。如果使用负索引,则有效索引为 0、1、2 和 3,以及 -1、-2、-3、-4。

由于2是有效的索引,您可以看到索引2中的项被打印出来,即C++:

my_list = ["Python","C","C++","JavaScript"]
print(my_list[2])

#Output
C++

如果您尝试访问位于有效索引范围之外的索引处的项,您将遇到​IndexError​:

print(my_list[4])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-7-437bc6501dea> in <module>()
      1 my_list = ["Python","C","C++","JavaScript"]
----> 2 print(my_list[4])

IndexError: list index out of range

如果您熟悉该模式,您现在将使用try和except来处理索引错误。

▶ 在下面的代码片段中,您尝试访问由 指定的索引处的项目search_idx。

search_idx = 3
try:
  print(my_list[search_idx])
except IndexError:
  print("Sorry, the list index is out of range")

这里,search_idx( 3) 是一个有效的索引,并且打印出特定索引处的项目:

JavaScript

如果search_idx超出索引的有效范围,则 except 块将 捕获IndexError为异常,并且不再有错误消息。

search_idx = 4
try:
  print(my_list[search_idx])
except IndexError:
  print("Sorry, the list index is out of range")

而是显示search_idx超出有效索引范围的消息:

Sorry, the list index is out of range

如何在 Python 中处理 KeyError(键值对异常)

在使用 Python 词典时可能遇到过KeyError。

▶ 考虑这个例子,你有一本字典my_dict。

my_dict ={"key1":"value1","key2":"value2","key3":"value3"}
search_key = "non-existent key"
print(my_dict[search_key])
  • 字典my_dict有 3 个键值对,"key1:value1", "key2:value2", 和"key3:value3"
  • 现在,您尝试访问字典并访问与 key 对应的值"non-existent key"。

正如预期的那样,你会得到一个KeyError:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-2-2a61d404be04> in <module>()
      1 my_dict ={"key1":"value1","key2":"value2","key3":"value3"}
      2 search_key = "non-existent key"
----> 3 my_dict[search_key]

KeyError: 'non-existent key'

几乎可以像处理IndexError一样处理KeyError.

  • 您可以尝试访问与指定的键对应的值search_key。
  • 如果search_key确实是一个有效的键,则打印出相应的值。
  • 如果由于密钥不存在而遇到异常,您可以使用该except块让用户知道。

这在下面的代码片段中进行了解释:

try:
  print(my_dict[search_key])
except KeyError:
  print("Sorry, that's not a valid key!")
Sorry, that's not a valid key!

▶ 如果您想提供其他操作,例如无效密钥的名称,您也可以这样做:密钥可能拼写错误,使其无效。在这种情况下,让用户知道使用的密钥可能会帮助他们修复拼写错误。

您可以通过捕获无效密钥<error_msg>并在发生异常时打印的消息中使用它来实现此目的:

try:
  print(my_dict[search_key])
except KeyError as error_msg:
  print(f"Sorry,{error_msg} is not a valid key!")

▶ 注意键名也是如何打印出来的:

Sorry,'non-existent key' is not a valid key!

如何在 Python 中处理 FileNotFoundError(文件找不到异常)

在 Python 中处理文件时发生的另一个常见错误是FileNotFoundError.

▶ 在以下示例中,您尝试通过指定open()函数的路径来打开文件my_file.txt。并且您想读取该文件并打印出该文件的内容。

但是,您尚未在指定位置创建文件。

如果您尝试运行下面的代码片段,您将得到FileNotFoundError:

my_file = open("/content/sample_data/my_file.txt")
contents = my_file.read()
print(contents)
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-4-4873cac1b11a> in <module>()
----> 1 my_file = open("my_file.txt")

FileNotFoundError: [Errno 2] No such file or directory: 'my_file.txt'

并使用try和 except,您可以执行以下操作:

  • 尝试在try块中打开文件。
  • 处理FileNotFoundError:通过except让用户知道他们试图打开一个不存在的文件块。
  • 如果try块成功,并且文件确实存在,则读取并打印出文件的内容。
  • 在finally块中,关闭文件,以免浪费资源。回想一下文件将如何关闭,而不管文件打开和读取步骤中发生了什么。
try:
  my_file = open("/content/sample_data/my_file.txt")
except FileNotFoundError:
  print(f"Sorry, the file does not exist")
else:
  contents = my_file.read()
  print(contents)
finally:
  my_file.close()

请注意您是如何将错误作为异常处理的,程序结束时会优雅地显示以下消息:

Sorry, the file does not exist

▶ 让我们考虑else触发块的情况。该文件my_file.txt现在位于前面提到的路径中。

这是文件my_file.txt包含的内容:

现在,重新运行之前的代码片段按预期工作。

这一次,文件my_file.txt存在,else块被触发并打印出其内容,如下所示:

我希望这能阐明您在处理文件时如何处理异常。