Python3.11教程4:异常处理

文章目录

    • 九、异常处理
      • 9.1 异常堆栈跟踪
      • 9.2 异常处理的基本语法
      • 9.3 异常类及异常处理机制
      • 9.4 自定义异常类
      • 9.5 raise触发异常,及其与except的区别
      • 9.6 异常链
      • 9.7 处理多个不相关的异常
      • 9.8 用注释细化异常情况
      • 9.9 异常处理的最佳实践

  • Python 3.11.5 中文文档、cpython源代码
  • 错误和异常、内置异常

九、异常处理

9.1 异常堆栈跟踪

  异常是指在程序执行过程中发生的不寻常或错误的事件。当程序在运行中发生异常时,Python会生成一个异常对象,其中包含了关于异常的信息,比如异常类型、发生异常的代码位置等。

  异常堆栈跟踪是一份详细的报告,它记录了异常是如何传播的,从异常发生的地方开始,一直到最初的异常引发点。这份报告以栈的形式呈现,因此被称为“堆栈跟踪”。下面是一个常见的除0错误示例:

result = 10 / 0  				# 这会引发一个除零异常
result
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[16], line 1
----> 1 result = 10 / 0  				# 这会引发一个除零异常2 resultZeroDivisionError: division by zero

在这个示例中,会发生以下操作:

  1. 解析器(Python解释器)会尝试解析您的代码以执行它,但在遇到除0错误时,解析器会停止执行并生成一个错误消息。

  2. 这个错误消息是一个典型的Python异常堆栈跟踪:

    1. ZeroDivisionError: 这是异常的类型,表示发生了一个除零错误。
    2. Traceback (most recent call last): 这一行告诉我们以下内容是异常堆栈跟踪的开始。
    3. Cell In[16], line 1: 这一行显示了发生异常的代码位置。具体来说,在 “Cell In[16]” 中的第1行发生了异常。
    4. result = 10 / 0: 这是尝试执行的代码,它试图将10除以0,但由于除零错误,引发了异常。
    5. result: 这是在异常发生后的下一行的代码,但由于异常在前一行引发,所以这一行实际上不会执行。

  总结起来,这个错误消息告诉我们在尝试将10除以0时发生了除零错误(ZeroDivisionError)。异常堆栈跟踪还显示了异常发生的确切代码位置,这对于调试问题非常有帮助。

下面是一个稍微复杂一点的堆栈跟踪示例:

Traceback (most recent call last):File "main.py", line 10, in <module>division_result = divide(10, 0)File "main.py", line 6, in divideresult = numerator / denominator
ZeroDivisionError: division by zeroDuring handling of the above exception, another exception occurred:Traceback (most recent call last):File "main.py", line 12, in <module>log_exception()File "main.py", line 8, in log_exceptionraise Exception("An error occurred while logging the exception.")
Exception: An error occurred while logging the exception.

在这个示例中,发生了两个异常,并且堆栈跟踪显示了异常传播的路径:

  1. 最初的异常发生在 “main.py” 文件的第10行,其中我们尝试调用 divide(10, 0) 函数,但由于除以零,引发了 ZeroDivisionError 异常。这是堆栈跟踪的起始点。

  2. 堆栈跟踪接着显示了异常处理程序的执行情况。在 log_exception() 函数中,我们尝试记录异常,但由于代码中引发了另一个异常,所以出现了第二个异常。第二个异常是一个通用的 Exception,并且在堆栈跟踪中显示了详细信息。

从上述示例可以看出,异常堆栈跟踪有以下几个重要作用:

  1. 定位异常引发点: 它告诉我们异常是在哪个位置引发的。这对于找到问题的根本原因至关重要。

  2. 追踪异常传播路径: 堆栈跟踪显示了异常是如何传播的,从引发异常的地方到最终导致程序中止的地方,这有助于我们理解异常如何影响程序的执行流程。

有时候堆栈跟踪可能会非常复杂,特别是在大型项目中,但基本的原则是相同的:从底部向上查看,以了解异常的源头和传播路径。

  1. 调试信息: 堆栈跟踪通常包含有关引发异常的代码的行号和文件名,这有助于我们快速定位到异常发生的代码行,以便进行调试。

9.2 异常处理的基本语法

为了避免异常导致程序终止,我们引入了异常处理操作,它可以:

  1. 防止程序崩溃:当程序遇到异常时,异常处理机制可以防止程序因错误而突然停止运行,从而保持程序的稳定性。
  2. 提供信息:异常处理允许程序员捕获和记录异常的详细信息,以便调试和分析问题。
  3. 执行恢复操作:在某些情况下,程序可以通过异常处理来执行恢复操作,以使程序继续正常运行或采取适当的措施来处理异常情况

在Python等编程语言中,异常处理通常通过以下关键字和结构来实现:

  1. trytry语句通常包含可能引发异常的代码,当尝试执行时,若无异常,则跳过 except 子句,执行后续语句

  2. except:捕获异常,并定义相应的异常处理逻辑,而不会导致程序的终止。
    try代码块中发生异常时,except中的语句会被执行,即判断异常类型是否与 except 关键字后指定的异常相匹配:

    • 如果匹配上,则会执行 对应的except 子句,然后跳过 try/except 代码块,继续执行后续代码。
    • 如果不匹配,则它会被传递到外部的 try 语句中;如果没有找到处理程序,则抛出一个 未处理异常 ,执行将终止并输出错误信息。
    • try 语句可以有多个 except 子句 来为不同的异常指定处理程序。 但最多只有一个处理程序会被执行
  3. else:处理没有异常的情况,所以else 子句必须放在所有 except 子句 之后。若try块中的代码成功执行,且没有异常被引发,那么else中的代码块会被执行。

  4. finally:无论是否发生异常都会执行 finally语句块,即使在tryexcept块中有return语句也不例外。finally语句通常用于确保资源的正确清理,例如关闭文件或释放网络连接。

  通过合理使用这些结构,程序员可以在异常发生时采取适当的措施,以确保程序的稳定性和可维护性,编写出更健壮的应用程序。

  下面是一个简单的文件操作异常处理示例,我们尝试打开文件,并在except块中自定义了相应的异常处理逻辑:

try:# 将文件操作代码包装在try语句中,以捕获可能发生的异常with open("file.txt", "r") as file:content = file.read()
except FileNotFoundError:# 处理文件不存在的情况print("文件不存在。")
except PermissionError:# 处理权限问题print("没有权限执行此操作。")
except Exception as e:# 处理其他异常print("发生错误:", str(e))
else:# 如果没有异常发生,执行这里的代码print("文件操作成功。")
finally:# 无论是否发生异常,都会执行这里的代码print("文件操作完成。")

  在except块中,你也可以根据需要捕获特定类型的异常。这使您可以更精确地处理不同类型的问题。例如,如果只关心文件不存在的异常,可以捕获FileNotFoundError

try:with open("file.txt", "r") as file:content = file.read()
except FileNotFoundError:print("文件不存在。")

except 子句 可以用带圆括号的元组来指定多个异常,例如::

except (RuntimeError, TypeError, NameError):pass

   finallytry语句的可选子句,用于定义在所有情况下都必须要执行的清理操作(例如释放文件等外部资源),且不论 try 语句是否触发异常,都会执行 finally 子句。以下内容介绍了几种比较复杂的触发异常情景:

  • 如果 finally 子句中包含 break、continue 或 return 等语句,异常将不会被重新引发。

    def example():try:x = 1 / 0  # 会引发异常except ZeroDivisionError:print("Caught an exception")finally:print("Finally block executed")return 42  # 返回值来自 finally 子句,不是来自 try 子句result = example()
    print("Result:", result)  
    
    Caught an exception
    Finally block executed
    Result: 42
    
  • 如果执行 try 语句时遇到 break,、continue 或 return 语句,则 finally 子句在执行 break、continue 或 return 语句之前执行。

    def example():try:print("In try block")return 10finally:print("In finally block")result = example()
    print("Result:", result)  
    
    In try block
    In finally block
    Result: 10
    
  • 如果 finally 子句中包含 return 语句,则返回值来自 finally 子句的某个 return 语句的返回值,而不是来自 try 子句的 return 语句的返回值。

    def example():try:return 10finally:return 20  # 返回值来自 finally 子句,不是来自 try 子句result = example()
    print("Result:", result)  
    
    Result: 20
    

下面再举一个复杂的例子说明:

def divide(x, y):try:result = x / yexcept 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'

9.3 异常类及异常处理机制

  在Python中,异常是通过异常类的实例来表示的。异常类是一种用于组织和表示不同类型异常情况的方式。每个异常类都代表一种特定类型的错误或异常情况,例如ValueError表示数值错误。当程序中发生异常情况时,Python会创建一个与该异常情况相关的异常类的实例,并将其引发(raise)。

  Python为常见的错误情况定义了许多内置异常,以便在程序执行过程中处理错误和异常情况,这些类都继承自基类BaseException。整个异常类的层级结构如下:

BaseException├── BaseExceptionGroup├── GeneratorExit├── KeyboardInterrupt├── SystemExit└── Exception├── ArithmeticError│    ├── FloatingPointError│    ├── OverflowError│    └── ZeroDivisionError├── AssertionError├── AttributeError├── BufferError├── EOFError├── ExceptionGroup [BaseExceptionGroup]├── ImportError│    └── ModuleNotFoundError├── LookupError│    ├── IndexError│    └── KeyError├── MemoryError├── NameError│    └── UnboundLocalError
...
...

下面对部分异常类进行解释:

主要异常类描述
BaseException所有内置异常类的基类,通常不直接使用,而是派生其他异常类
GeneratorExit用于当生成器或协程在执行中遇到该异常时,自动关闭。通常用于清理生成器或协程中的资源
KeyboardInterrupt当用户在命令行中按下Ctrl+C时,会引发此异常,中断正在执行的程序。
SystemExit当调用sys.exit()函数时引发的异常,用于退出Python解释器。
Exception所有内置的非系统退出类的基类,所有用户自定义异常也应当派生自此类
常见异常类描述
SyntaxError语法错误
IndentationError缩进错误
ImportError导入模块不存在
NameError名称错误,当尝试访问未定义的变量或函数时引发。
AttributeError属性错误,当尝试访问对象没有的属性或方法时引发。
TypeError类型错误,当操作不兼容的数据类型时引发。
KeyError使用字典中不存在的键时引发。
ValueError当函数收到的参数类型正确但值不合法时引发。
ZeroDivisionError零除错误,当试图除以零时引发。
FileNotFoundError文件未找到错误
PermissionError文件权限不足错误
IndexError索引错误,当尝试访问不存在的列表元素时引发。
MemoryError内存耗尽异常。通常在处理大型数据集时发生。

  类有继承这一特性,所以若发生的异常与 except 子句中的类是同一个类或是它的基类时,则该类与该异常相兼容,反之则不成立。简单来说,就是子类兼容父类的异常,父类不兼容子类的异常。下面举例说明:

class B(Exception):passclass C(B):passclass D(C):passfor cls in [B, C, D]:try:raise cls()except D:print("D")except C:print("C")except B:print("B")

上面的代码将依次打印 B, C, D。而如果将except子句顺序改为:

 try:raise cls()except B:print("B")except C:print("C")except D:print("D")

  则会打印出B,B,B。这是因为C和D都是B的子类,能捕获B类的错误,肯定也会捕获C类和D类,所以三次遍历都会被except B捕获。

9.4 自定义异常类

  内置异常类可以被子类化以定义新的异常,但是在创建自定义异常类时,推荐从Exception类或其子类来继承,而不是直接继承自BaseException类。这是因为Exception 是所有非致命异常的基类,而BaseException类和它的其它子类表示的都是非常严重的异常,通常会导致程序终止,也就不需要进行处理。

  Exception 类具有一个构造函数 __init__(self, *args),该构造函数允许传递一个或多个参数(通常是异常消息)。你可以在派生的自定义异常类中扩展此构造函数以添加额外的参数,如错误代码、时间戳等。

  异常类命名一般都以 “Error” 结尾,与标准异常的命名保持一致。异常类应当保持简单,只提供一些属性,允许相应的异常处理程序提取有关错误的信息。许多标准模块都定义了自己的异常,以报告他们定义的函数中可能出现的错误。

  1. 自定义属性,以提供有关错误的更多上下文信息
import datetime# 自定义一个简单的异常类,继承自 Exception
class CustomError(Exception):# message用于提供异常的描述性消息,error_code用于指定自定义错误代码。def __init__(self, message, error_code):# super调用父类 Exception 的构造函数init,并传递 message 参数,以初始化异常的消息。super().__init__(message)self.error_code = error_code# 获取当前时间戳,并将其赋值给异常对象的 timestamp 属性self.timestamp = datetime.datetime.now()# 将异常对象的 file_name 属性初始化为 None,目前还没有指定文件名。self.file_name = Nonetry:# 使用 raise 语句抛出了一个 CustomError 异常的实例。# 异常的消息是 "This is a custom exception",错误代码是 1001。raise CustomError("This is a custom exception", 1001)
except CustomError as ce:# 打印了异常的消息、错误代码和时间戳print(f"Custom error occurred: {ce}, Error code: {ce.error_code}, Timestamp: {ce.timestamp}")
# 自定义FileNotFoundError类,用于捕获异常时获取文件名信息
class FileNotFoundError(Exception):def __init__(self, file_name):super().__init__(f"File not found: {file_name}")file_name = "example.txt"
try:# 尝试打开文件with open(file_name, "r") as file:content = file.read()
except FileNotFoundError as fnfe:print(fnfe)
  1. 自定义方法,执行与异常处理相关的操作,例如记录异常、发送通知、自动修复等。
class CustomError(Exception):def __init__(self, message, error_code):super().__init__(message)self.error_code = error_codedef log_error(self):# 将异常信息记录到日志文件#  打开'error.log' 日志文件,模式为 'a'(追加)with open('error.log', 'a') as log_file:log_file.write(f"Error Code: {self.error_code}, Message: {str(self)}\n")def notify_admin(self):# 发送电子邮件或其他通知给管理员pass
try:raise CustomError("This is a custom exception", 1001)
except CustomError as ce:ce.log_error()ce.notify_admin()print(f"Custom error occurred: {ce}, Error code: {ce.error_code}")

9.5 raise触发异常,及其与except的区别

  除了使用try...except语句,你也可以使用 raise 语句来手动引发指定的异常,其唯一的参数就是要触发的异常,但必须是异常实例或异常类。

  1. 引发异常实例:您可以使用 raise 语句引发已经创建的某个异常类的实例,这样做的目的是在特定的代码位置引发异常,并且可以提供关于异常的详细信息。

    # 创建一个自定义异常类
    class CustomError(Exception):def __init__(self, message):super().__init__(message)try:# 创建异常实例并引发error_instance = CustomError("This is a custom exception")raise error_instance
    except CustomError as ce:print(f"Caught custom exception: {ce}")
    
  2. 引发异常类:当您想引发某种标准异常时,但不需要提供额外的异常信息时,可以简单地引发异常类。

    try:# 引发内置的 ValueError 异常类raise ValueError("This is a ValueError")
    except ValueError as ve:print(f"Caught ValueError: {ve}")
    

  某些时候,我们想要在捕获异常后,继续将同一异常传播到更高级别的异常处理程序,或者让其他部分的代码处理它。这时,我们可以使用 raise 语句来重新引发已捕获的异常

try:result = 10 / 0  				# 这会引发一个除零异常
except ZeroDivisionError:print("Divided by zero")# 当异常在 except 块中被捕获后,可以使用 raise 语句重新引发相同的异常,以便允许其他代码或更高级别的异常处理程序来处理异常。raise							
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[14], line 21 try:
----> 2     result = 10 / 0  				# 这会引发一个除零异常3 except ZeroDivisionError:4     print("Divided by zero")ZeroDivisionError: division by zero

raiseexcept都用于处理异常,但它们的角色和使用场景是不同的:

  • except:用于捕获和处理在 try 块中引发的异常。这是一种被动的操作,它用于响应异常的发生。你可以在except 语句中定义异常处理逻辑,例如指定不同的 except 子句处理不同类型的异常。如果不处理异常,程序将终止并显示异常信息。

  • raise:用于在代码中明确指出异常情况的发生,这是一种主动的操作,你可以在任何地方使用它来引发异常。raise 通常用于异常的起始点,它告诉程序出现了异常情况,然后程序的控制权会被传递给异常处理程序(except 子句)。

  下面是一个具体的示例。假设一个函数,接受一个数字作为参数,它在参数为负数时应该引发一个自定义异常。这里就是 raise 的使用场景,它用于主动引发异常:

def divide_positive_numbers(a, b):if a < 0 or b < 0:raise ValueError("Both numbers should be positive")return a / b

然后,在调用这个函数时,可以使用 tryexcept 来捕获和处理这个异常:

try:result = divide_positive_numbers(-5, 2)
except ValueError as e:print("An error occurred:", e)

  在这个示例中,raise 用于在函数内部检测到不符合要求的输入时引发异常,而 except 用于捕获和处理这个异常。这就是 raiseexcept 在异常处理中的不同用途和角色

9.6 异常链

  异常链是指在 Python 中,一个异常引发另一个异常的情况,从而形成一个异常的嵌套链条。这个链条可以用来跟踪异常的源头,以便更好地理解程序中发生的错误,特别是当异常发生在复杂的程序堆栈中时。通过正确使用 raise 语句的 from 子句,可以更好地组织和管理异常链。下面是常见的异常链操作:

  1. 使用 from子句触发异常链: 你可以在 raise 语句中使用 from 子句,将一个异常视为另一个异常的原因。这样,新引发的异常将成为前一个异常的 “直接原因”。

    # 示例1:异常的直接后果和异常链
    try:# 引发一个异常raise ValueError("This is the first exception")
    except ValueError as ve:try:# 引发另一个异常,将前一个异常作为直接的原因raise TypeError("This is the second exception") from veexcept TypeError as te:print("Caught TypeError:", te)print("Direct cause:", te.__cause__)
    

  在这个示例中,我们首先引发一个 ValueError 异常,然后在 except 块中引发一个 TypeError 异常,并将前一个异常 ve 作为直接的原因。这样,我们就可以在 TypeError 异常中通过 te.__cause__ 访问到直接的原因,即 ValueError 异常。

  1. 使用 from None 禁用自动异常链:如果在 raise 语句的 from 子句中使用 None,则会禁止Python自动创建异常链,以减少异常的复杂性。此时,异常会被视为相互独立的。

    try:# 引发一个异常,并禁用自动异常链raise ValueError("This is an exception") from None
    except ValueError as ve:print("Caught ValueError:", ve)print("Direct cause:", ve.__cause__)
    

    在这个示例中,异常链被禁用,ve.__cause__ 的值为 None,不再与其他异常相关联。

9.7 处理多个不相关的异常

  在某些情况下,可能需要报告多个已经发生的异常,而不仅仅只是报告第一个异常。例如在并发框架等情境中,多个任务并行执行时,可能会发生多个错误。

  为了处理这种情况,Python 提供了一种 ExceptionGroup,它允许将多个异常实例打包成一个列表,从而一起引发,这使得多个异常可以同时被捕获和处理。

def f():# 创建了一个包含两个异常实例的列表,这两个异常实例都包含了错误消息excs = [OSError('error 1'), SystemError('error 2')]raise ExceptionGroup('there were problems', excs)f()
---------------------------------------------------------------------------
ExceptionGroup                            Traceback (most recent call last)
Cell In[17], line 52     excs = [OSError('error 1'), SystemError('error 2')]3     raise ExceptionGroup('there were problems', excs)
----> 5 f()Cell In[17], line 3, in f()1 def f():2     excs = [OSError('error 1'), SystemError('error 2')]
----> 3     raise ExceptionGroup('there were problems', excs)ExceptionGroup: there were problems (2 sub-exceptions)

  上述代码使用 raise ExceptionGroup 引发了一个异常,该异常包含一个错误消息 ‘there were problems’ 和前面创建的异常实例列表 excs。这样就创建了一个异常链,其中 ExceptionGroup 是顶级异常,而 OSError 和 SystemError 是其下的子异常。

使用以下代码,可以打印具体的异常信息:

def f():excs = [OSError('error 1'), SystemError('error 2')]raise ExceptionGroup('there were problems', excs)try:f()
except Exception as e:print(f'caught {type(e).__name__}: {e}')  # 打印异常信息# 如果异常是 ExceptionGroup,打印其中包含的子异常信息if isinstance(e, ExceptionGroup):for i, exc in enumerate(e.exceptions, 1):print(f'Exception {i}: {type(exc).__name__}: {exc}')
caught ExceptionGroup: there were problems (2 sub-exceptions)
Exception 1: OSError: error 1
Exception 2: SystemError: error 2

  对于一个嵌套的异常组,可以使用except* 子句从组中提取了某种类型的异常,而让所有其他的异常传播到其他子句,并最终被重新引发。另外,嵌套在一个异常组中的异常必须是实例,而不是类型。这是因为在实践中,这些异常通常是那些已经被程序提出并捕获的异常。此部分详细内容,请参考《错误和异常》。

9.8 用注释细化异常情况

  在异常被捕获后,有时需要向异常对象添加额外的信息,以便更好地描述错误和提供上下文信息。Python 的异常对象具有 add_note(note) 方法,允许您将字符串注释添加到异常对象的注释列表中。当异常被引发后,异常对象中的注释会按照它们被添加的顺序显示在标准异常回溯信息中,例如:

try:raise TypeError('bad type')
except Exception as e:e.add_note('Add some information')e.add_note('Add some more information')raise
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[21], line 21 try:
----> 2     raise TypeError('bad type')3 except Exception as e:4     e.add_note('Add some information')TypeError: bad type
Add some information
Add some more information

你也可以在自定义异常中,添加额外的异常注释:

class CustomError(Exception):def __init__(self, message):super().__init__(message)self.notes = []  # 初始化注释列表def add_note(self, note):self.notes.append(note)  # 添加注释到异常对象的注释列表中def divide(a, b):try:result = a / breturn resultexcept ZeroDivisionError as zd_err:# 创建自定义异常并添加注释custom_err = CustomError("Division error")custom_err.add_note("Attempted to divide by zero")raise custom_errtry:result = divide(10, 0)
except CustomError as ce:print("Caught CustomError:", ce)print("Notes:")for note in ce.notes:print("-", note)
Caught CustomError: Division error
Notes:
- Attempted to divide by zero

  在这个示例中,divide 函数尝试执行除法操作,但如果除法操作失败(除以零),它会引发一个自定义异常 CustomError。在这个自定义异常中,我们使用 add_note 方法添加了两个注释,分别描述了错误的性质和错误的上下文。最后,在异常处理代码中,我们遍历异常对象的注释列表,以查看所有添加的注释。

9.9 异常处理的最佳实践

  在编写Python代码时,异常处理是一个关键的方面,它有助于增加代码的稳定性和可靠性,能够处理各种不可预测的情况,增加代码的健壮性。以下是一些建议:

  1. 仅捕获您知道如何处理的异常,以便精确处理问题
    在异常处理中,应该只捕获那些您知道如何处理的异常。不要仅仅因为可能发生异常就捕获所有异常。这可能会导致隐藏真正的问题,使调试变得更加困难。只捕获你能够处理的异常,对于其他异常,应该让它们引发并停止程序的执行。

  2. 引发具有明确描述的自定义异常
    当密需要在代码中指示特定的错误条件时,应该引发自定义异常。这些异常应该具有清晰的描述,以便其他开发人员能够理解问题的本质。例如:

    if condition:raise CustomException("This is a custom exception message.")
    
  3. 不要捕获 Exception 的通用异常
    避免捕获通用的 Exception 异常,因为这样会捕获所有异常,包括系统退出等。这可能导致程序的行为不可预测。应该捕获具体的异常类型,例如 ValueErrorFileNotFoundError 等。

  4. 记录异常信息
    当捕获异常时,应该记录异常信息,以便后续调试。可以使用标准库中的 logging 模块来记录异常信息,以便更容易诊断问题。例如:

    import logging  								
    # 配置日志记录,将日志保存到名为myapp.log的文件中,只记录ERROR级别及以上的日志,并使用特定的格式
    logging.basicConfig(filename='myapp.log', level=logging.ERROR, format='%(asctime)s [%(levelname)s] %(message)s')try:result = 10 / 0  							# 这会引发一个除零异常	 
    except ZeroDivisionError as e:					# 捕获ZeroDivisionError异常,并将其赋给变量e	    	    logging.error("An error occurred: %s", e)	# 使用logging.error()记录错误级别的日志,包括异常信息	    
    

  上述代码中,format 参数用于定义日志消息的格式,它是一个包含占位符的字符串,这些占位符将在实际记录日志消息时被替换为相应的值。常用参数包括:

占位符含义
%s用于插入字符串。
%d用于插入整数。
%f用于插入浮点数。
%r用于插入表示对象的字符串,通常是 repr(object) 的结果。
%(name)s用于插入命名的变量值,其中 name 是一个变量名。
%(asctime)s时间戳,通常带有日期和时间的字符串。asctime 表示 “人类可读的时间”,通常包括日期和时间。
%(levelname)s日志消息级别,例如 “ERROR”、“INFO” 等。
%(message)s日志消息的主体部分。

  最后,logging.error()这是调用日志记录模块中的 error 方法,表示记录一个错误级别的日志消息,s%表示字符串格式。通过设置以上格式,最终输出的日志为以下内容,并被记录在myapp.log文件中:

# 519表示毫秒
2023-09-07 13:36:06,519 [ERROR] An error occurred: division by zero
  1. 使用 finally 块进行清理操作
    如果你有需要在无论是否发生异常都要执行的清理代码,可以使用 finally 块,这确保了清理代码始终会执行,即使发生异常也不例外。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/70738.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

儿童安全门和围栏,以及游戏围栏等美国站要求的合规标准是什么?

儿童安全门和围栏 儿童安全门和围栏用于在门口&#xff08;如门道&#xff09;内设置围栏&#xff0c;或用作自支撑围栏&#xff0c;将幼儿可能在其中活动的区域围起来。这些商品可能由塑料、金属、乙烯树脂或木制组件等材料制成。此政策包括但不限于可扩展围栏、伸缩安全门和…

初识Java 6-1 复用

目录 组合 继承 委托 组合和继承的结合 确保正确的清理 名称隐藏 在组合和继承之间选择 protected关键字 向上转型 final关键字 final数据 final方法 final类 初始化及类的重载 本笔记参考自&#xff1a; 《On Java 中文版》 对面向对象的编程语言而言&#xff0…

arm-none-eabi-size 查看内存映像大小

目录 一、概述 1.1 功能1.2 命令格式1.3 支持的目标文件 二、基本应用示例 2.1 查看单个对象文件2.2 查看整个工程的内存映像 三、命令选项描述 3.1 以不同的进制格式显示内存映像 示例&#xff1a;以十六进制格式显示 3.2 不同的输出方式 3.2.1 可选的表示方式与对应的选项3.…

Windows系统管理一:操作系统概述

计算机系统的层次结构 五大基本功能 处理器管理 其工作主要是进程调度&#xff0c;在单用户单任务的情况下&#xff0c;必处理器仅为一个用户的一个任务所独占&#xff0c;进程管理的工作十分简单。但在多道程序或多用户的情况下&#xff0c;组织多个作业或任务时&#xff0c…

AJAX学习笔记8 跨域问题及解决方案

AJAX学习笔记7 AJAX实现省市联动_biubiubiu0706的博客-CSDN博客 跨域:指一个域名的网页去请求另外一个域名资源.比如百度页面去请求京东页面资源. 同源与不同源三要素:协议,域名,端口 协议一致,域名一致,端口一致.才算是同源.其他一律不同源 新建项目测试: 1.window.open();…

算法通关村第十六关:白银挑战-滑动窗口经典问题

白银挑战-滑动窗口经典问题 1. 最长子串专题 1.1 无重复字符的最长子串 LeetCode3 思路分析 要找最长子串&#xff0c;需要直到无重复字符串的首和尾&#xff0c;然后再确定最长的那个&#xff0c;需要两个指针&#xff0c;可利用滑动窗口思想 方法1&#xff1a;集合 建…

layui--记录

layui 行点击事件&#xff1a;点了没反应&#xff1f; //监听行工具事件layui.table.on(tool(demo), function (obj) {//alert(222) });原因&#xff1a;检查下id与lay-filter是否一致&#xff1b;id与lay-filter必须一致。 <table id"demo" lay-filter"dem…

Yarn资源调度器

文章目录 一、Yarn资源调度器1、架构2、Yarn工作机制3、HDFS、YARN、MR关系4、作业提交之HDFS&MapReduce 二、Yarn调度器和调度算法1、先进先出调度器&#xff08;FIFO&#xff09;2、容量调度器&#xff08;Capacity Scheduler&#xff09;3、公平调度器&#xff08;Fair …

业务调整,业绩短期承压,市场热潮退去后的乐舱物流将驶向何方?

撰稿|行星 来源|贝多财经 9月5日&#xff0c;乐舱物流股份有限公司&#xff08;下称“乐舱物流”&#xff09;通过港交所上市聆讯&#xff0c;并披露了通过港交所聆讯后的资料集&#xff08;即招股书&#xff09;&#xff0c;中信证券和农银国际为其联席保荐人。 成立于2004…

pdf怎么转换成dwg格式?简单转换方法分享

当我们需要在CAD中编辑PDF文件中的向量图形时&#xff0c;将PDF转换成DWG格式是一个非常好的选择。因为PDF是一种非常流行的文档格式&#xff0c;很多时候我们会接收到PDF文件&#xff0c;但是PDF文件中的向量图形无法直接在CAD中编辑。而将PDF转换成DWG格式后&#xff0c;就可…

【网络层】网络基础 -- IP协议

引入IP协议头格式网段划分特殊的IP地址IP地址的数量限制 私有IP地址和公网IP地址分片与组装如何分片与组装&#xff1f; 引入 我们前面学习了传输层的相关知识&#xff0c;难道真的就是直接传送吗&#xff1f;当然不是&#xff0c;那TCP究竟做了什么&#xff1f;IP又扮演什么角…

图床项目详解

文章目录 一、图床项目介绍二、图床项目架构三、图床功能实现3.1 注册功能3.2 登录功能3.3 用户文件列表3.4 上传文件3.5 上传文件之秒传3.6 获取共享文件列表或下载榜3.7 分享/ 删除文件/ 更新下载数3.8 取消分享/ 转存/ 更新下载计数3.9 图床分享图片 一、图床项目介绍 实现…

想考PMP,符合报名条件么?怎么报考?

报考PMP第一步就是了解报名条件&#xff1a; PMP考试如何报名&#xff1f; 先在PMI官网报英文报名&#xff0c;再在人才交流基金会上报中文报名以及缴费。 1、英文报名 PMP英文报名就是在PMI网站上提交对应的英文材料信息。不限时间&#xff0c;随时可以报名&#xff0c;报…

2023年高教社杯 国赛数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

leetcode645. 错误的集合(java)

错误的集合 题目描述优化空间代码演示 题目描述 难度 - 简单 LC645 - 错误的集合 集合 s 包含从 1 到 n 的整数。不幸的是&#xff0c;因为数据错误&#xff0c;导致集合里面某一个数字复制了成了集合里面的另外一个数字的值&#xff0c;导致集合 丢失了一个数字 并且 有一个数…

qt : day 3

1.完成登录框的按钮操作&#xff0c;并在登录成功后进行界面跳转 ------------------------------------------------------------------ .pro ------------------------------------------------------------------ QT core gui texttospeech greaterThan(QT_MAJOR_V…

Talk | ICCV‘23南洋理工大学博士后李祥泰:面向统一高效的视频分割方法设计

本期为TechBeat人工智能社区第528期线上Talk&#xff01; 北京时间9月6日(周三)20:00&#xff0c;南洋理工大学博士后研究员—李祥泰的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “面向统一高效的视频分割方法设计”&#xff0c;他分享了其在视…

华为云云服务器评测|安装Java8环境 配置环境变量 spring项目部署 【!】存在问题未解决

目录 引出安装JDK8环境查看是否有默认jar上传Linux版本的jar包解压压缩包配置环境变量 上传jar包以及运行问题上传Jar包运行控制台开放端口访问失败—见问题记录关闭Jar的方式1.进程kill -92.ctrl c退出 问题记录&#xff1a;【!】未解决各种方式查看端口情况联系工程师最后排查…

在Ubuntu Linux系统上安装RabbitMQ服务并解决公网远程访问问题

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

智能电销机器人,主要体现的价值是什么

21世纪科技的迅速发展&#xff0c;人工智能逐渐走入大家的视线&#xff0c;越来越多的机器人出现在我们生活中。见的最多的有电销公司的智能语音机器人、在仓库拣货打包的机器人、商场店铺供娱乐对话的机器人。机器人活跃在各行各业中&#xff0c;降低了人工成本&#xff0c;代…