企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# Python 异常 > 原文: [http://zetcode.com/lang/python/exceptions/](http://zetcode.com/lang/python/exceptions/) 在 Python 教程的这一部分中,我们讨论 Python 中的异常。 执行期间检测到的错误称为异常。 在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 互联网连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为防止这种情况,我们必须应对可能发生的所有可能的错误。 为此,我们可以使用异常处理。 ## 在 Python 中捕捉异常 在 Python 中,我们具有以下语法来处理异常: ```py try: # do something except ValueError: # handle ValueError exception except (IndexError, ZeroDivisionError): # handle multiple exceptions # IndexError and ZeroDivisionError except: # handle all other exceptions finally: # cleanup resources ``` 我们期望发生异常的代码写在`try`块中。 `except`关键字捕获程序中指定的或剩余的异常。 始终执行可选的`finally`块; 它用于清除资源,例如打开的文件或数据库连接。 ## `ZeroDivisionError` 除以零是不可能的。 如果我们尝试执行此操作,则会引发`ZeroDivisionError`并中断脚本。 > **注意**:以下示例演示了异常在 Python 中的工作方式。 确保除数不为零而不是捕获`ZeroDivisionError`更直接。 `zero_division.py` ```py #!/usr/bin/env python # zero_division.py def input_numbers(): a = float(input("Enter first number:")) b = float(input("Enter second number:")) return a, b x, y = input_numbers() print(f"{x} / {y} is {x/y}") ``` 在此脚本中,我们从控制台获得两个数字。 我们将这两个数相除。 如果第二个数字为零,则会出现异常。 ```py Enter first number:3 Enter second number:0 Traceback (most recent call last): File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 14, in <module> print(f"{x} / {y} is {x/y}") ZeroDivisionError: float division by zero ``` 我们可以通过两种方式处理此问题。 `zero_division2.py` ```py #!/usr/bin/env python # zero_division2.py def input_numbers(): a = float(input("Enter first number:")) b = float(input("Enter second number:")) return a, b x, y = input_numbers() while True: if y != 0: print(f"{x} / {y} is {x/y}") break else: print("Cannot divide by zero") x, y = input_numbers() ``` 首先,我们仅检查`y`的值不为零。 如果`y`值为零,我们将打印警告消息并再次重复输入周期。 这样我们就可以处理错误,并且脚本不会中断。 ```py $ ./zero_division2.py Enter first number:4 Enter second number:0 Cannot divide by zero Enter first number:5 Enter second number:0 Cannot divide by zero Enter first number:5 Enter second number:6 5.0 / 6.0 is 0.8333333333333334 ``` 另一种方法是使用异常。 `zero_division3.py` ```py #!/usr/bin/env python # zerodivision3.py def input_numbers(): a = float(input("Enter first number:")) b = float(input("Enter second number:")) return a, b x, y = input_numbers() try: print(f"{x} / {y} is {x/y}") except ZeroDivisionError: print("Cannot divide by zero") x, y = input_numbers() ``` 我们将代码放置在`try`关键字之后我们期望发生异常的位置。 如果引发了`except`关键字,则捕获该异常。 异常类型在`except`关键字之后指定。 ```py except ValueError: pass except (IOError, OSError): pass ``` 要处理更多的异常,我们可以使用除关键字之外的更多异常,也可以将异常名称放在元组中。 ## `ValueError` 当内置操作或函数接收到类型正确但值不合适的自变量时,会引发`ValueError`,并且无法通过更精确的异常描述这种情况。 `value_error.py` ```py #!/usr/bin/env python # value_error.py def read_age(): age = int(input("Enter your age: ")) if age < 0 or age > 130: raise ValueError("Invalid age") return age try: val = read_age() print(f"Your age is {val}") except ValueError as e: print(e) ``` 在该示例中,我们有一个读取年龄作为用户输入的函数。 当用户提供不正确的值时,我们将引发`ValueError`异常。 ```py if age < 0 or age > 130: raise ValueError("Invalid age") return age ``` 负年龄是没有道理的,在现代没有记录到年龄超过 130 岁的人。 ## Python 多个异常 可以在一个`except`子句中捕获多个异常。 `multiple_exceptions.py` ```py #!/usr/bin/env python # multiple_exceptions.py import os try: os.mkdir('newdir') print('directory created') raise RuntimeError("Runtime error occurred") except (FileExistsError, RuntimeError) as e: print(e) ``` 该代码示例在一个`except`语句中捕获了两个异常:`FileExistsError`和`RuntimeError`。 ```py os.mkdir('newdir') ``` 使用`os.mkdir()`方法创建一个新目录。 如果目录已经存在,则触发`FileExistsError`。 ```py raise RuntimeError("Runtime error occurred") ``` 我们使用`raise`关键字手动引发运行时异常。 ## Python 异常参数 异常可以具有关联的值,该值指示错误的详细原因。 该值放在`as`关键字之后。 `exception_as.py` ```py #!/usr/bin/env python # exception_argument.py try: a = (1, 2, 3, 4) print(a[5]) except IndexError as e: print(e) print("Class:", e.__class__) ``` 从异常对象中,我们可以获取错误消息或类名。 ```py $ ./exception_as.py tuple index out of range Class: <class 'IndexError'> ``` ## Python 异常层次结构 异常是按层次结构组织的,它们是所有异常的父级`Exception`。 `interrupt.py` ```py #!/usr/bin/env python # interrupt.py try: while True: pass except KeyboardInterrupt: print("Program interrupted") ``` 脚本开始并不断循环。 如果按 `Ctrl + C` ,我们将中断循环。 在这里,我们捕获了`KeyboardInterrupt`异常。 ```py Exception BaseException KeyboardInterrupt ``` 这是`KeyboardInterrupt`异常的层次结构。 `interrupt2.py` ```py #!/usr/bin/env python # interrupt2.py try: while True: pass except BaseException: print("Program interrupted") ``` 这个例子也可以。 `BaseException`也捕获键盘中断; 除其他异常。 但是,这不是一个好习惯。 我们应该在`except`子句中捕获特定的异常。 ## Python 用户定义的异常 如果需要,我们可以创建自己的异常。 我们通过定义一个新的异常类来做到这一点。 `user_defined.py` ```py #!/usr/bin/env python # user_defined.py class BFoundEx(Exception): def __init__(self, value): self.par = value def __str__(self): return f"BFoundEx: b character found at position {self.par}" string = "There are beautiful trees in the forest." pos = 0 for i in string: try: if i == 'b': raise BFoundEx(pos) pos = pos + 1 except BFoundEx as e: print(e) ``` 在我们的代码示例中,我们创建了一个新的异常。 异常是从基类`Exception`派生的。 如果在字符串中发现字母`b`的任何出现,我们将`raise`作为异常。 ```py $ ./user_defined.py 'BFoundEx: b character found at position 10' ``` ## 清理 有一个`finally`关键字,始终会执行。 不管是否引发异常。 它通常用于清理程序中的资源。 `cleanup.py` ```py #!/usr/bin/env python # cleanup.py f = None try: f = open('data.txt', 'r') contents = f.readlines() for line in contents: print(line.rstrip()) except IOError: print('Error opening file') finally: if f: f.close() ``` 在我们的示例中,我们尝试打开一个文件。 如果我们无法打开文件,则会弹出`IOError`。 如果我们打开文件,我们想关闭文件处理器。 为此,我们使用`finally`关键字。 在`finally`块中,我们检查文件是否已打开。 如果打开了,我们将其关闭。 当我们使用数据库时,这是一种常见的编程结构。 在那里,我们类似地清理打开的数据库连接。 ## 栈跟踪 栈跟踪显示了引发未捕获的异常时的调用栈(至此为止已调用的函数栈)。 Python `traceback`模块提供了一个标准接口,用于提取,格式化和打印 Python 程序的栈跟踪。 它精确地模仿了 Python 解释器在打印栈跟踪时的行为。 `stacktrace_ex.py` ```py #!/usr/bin/env python # stacktrace_ex.py import traceback def myfun(): def myfun2(): try: 3 / 0 except ZeroDivisionError as e: print(e) print("Class:", e.__class__) for line in traceback.format_stack(): print(line.strip()) myfun2() def test(): myfun() test() ``` 在示例中,我们在嵌套的`myfun2`函数中将除以零的异常。 ```py for line in traceback.format_stack(): ``` `format_stack()`从当前栈帧中提取原始回溯并将其格式化为元组列表。 我们使用`for`循环遍历元组列表。 ```py $ ./stacktrace_ex.py division by zero Class: <class 'ZeroDivisionError'> File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 30, in <module> test() File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 27, in test myfun() File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 23, in myfun myfun2() File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 20, in myfun2 for line in traceback.format_stack(): ``` 在程序中,我们可以看到调用栈-导致错误的被调用函数的顺序。 在本章中,我们介绍了 Python 中的异常。