Python3.11 Tutorial 4: Exception Handling

9. Exception handling

9.1 Exception stack trace

  Exceptions are unusual or erroneous events that occur during program execution. When an exception occurs in the program, Python will generate an exception object, which contains information about the exception, such as the exception type, the code location where the exception occurred, and so on.

  An exception stack trace is a detailed report of how an exception was propagated from where it occurred to the point where it was originally raised. This report is presented as a stack, hence the name "stack trace". Here is an example of a common divide-by-zero error:

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

In this example, the following happens:

  1. The parser (Python interpreter) tries to parse your code to execute it, but when it encounters a division by zero error, the parser stops execution and generates an error message.

  2. This error message is a typical Python exception stack trace:

    1. ZeroDivisionError: This is the type of exception indicating that a divide-by-zero error occurred.
    2. Traceback (most recent call last): This line tells us that the following is the beginning of the exception stack trace.
    3. Cell In[16], line 1: This line shows the code location where the exception occurred. Specifically, the exception occurred at line 1 in "Cell In[16]".
    4. result = 10 / 0: This is the code that tries to execute, it tries to divide 10 by 0, but due to a division by zero error, an exception is thrown.
    5. result: This is the next line of code after the exception occurs, but since the exception was raised on the previous line, this line will not actually execute.

  To summarize, this error message tells us that a division-by-zero error (ZeroDivisionError) occurred while trying to divide 10 by 0. Exception stack traces also show the exact code location where the exception occurred, which can be very helpful when debugging problems.

Here's an example of a slightly more complex stack trace:

Traceback (most recent call last):
  File "main.py", line 10, in <module>
    division_result = divide(10, 0)
  File "main.py", line 6, in divide
    result = numerator / denominator
ZeroDivisionError: division by zero

During 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_exception
    raise Exception("An error occurred while logging the exception.")
Exception: An error occurred while logging the exception.

In this example, two exceptions occurred, and the stack trace shows the path the exceptions took:

  1. The initial exception occurs on line 10 of the "main.py" file, where we try to call divide(10, 0)a function, but due to division by zero, an exception is thrown ZeroDivisionError. This is the starting point of the stack trace.

  2. The stack trace then shows the execution of the exception handler. In log_exception()the function, we try to log an exception, but a second exception occurs because another exception is raised in the code. The second exception is a generic one Exceptionand the details are shown in the stack trace.

As can be seen from the above example, exception stack traces have the following important functions:

  1. Locating the exception trigger point : It tells us where the exception was raised. This is critical to getting to the root cause of the problem.

  2. Tracing the exception propagation path : A stack trace shows how an exception propagates, from where it is raised to where it ultimately causes the program to terminate, which helps us understand how the exception affects the execution flow of the program.

Sometimes stack traces can be very complex, especially in large projects, but the basic principle is the same: look from the bottom up to understand the origin and propagation path of the exception.

  1. Debugging information : Stack traces usually contain the line number and file name of the code that caused the exception, which helps us quickly locate the line of code where the exception occurred for debugging.

9.2 Basic syntax for exception handling

In order to avoid exceptions causing program termination, we introduce exception handling operations, which can:

  1. Prevent the program from crashing: When the program encounters an exception, the exception handling mechanism can prevent the program from suddenly stopping due to an error, thereby maintaining the stability of the program.
  2. Providing information: Exception handling allows programmers to capture and log exception details for debugging and analyzing problems.
  3. Perform recovery operations: In some cases, the program can perform recovery operations through exception handling to allow the program to continue running normally or to take appropriate measures to handle the abnormal situation.

In programming languages ​​such as Python, exception handling is usually implemented through the following keywords and structures:

  1. try: tryStatements usually contain code that may cause exceptions. When trying to execute, if there is no exception, the except clause will be skipped and subsequent statements will be executed.

  2. except: Catch exceptions and define corresponding exception handling logic without causing program termination.
    When an exception occurs in trythe code block, the statement in except will be executed, that is, to determine whether the exception type matches the exception specified after the except keyword:

    • If there is a match, the corresponding except clause will be executed, then the try/except code block will be skipped, and subsequent code execution will continue.
    • If there is no match, it is passed to the outer try statement; if no handler is found, an unhandled exception is thrown and execution terminates with an error message.
    • A try statement can have multiple except clauses to specify handlers for different exceptions. But at most one handler will be executed
  3. else: handles no exceptions, so the else clause must be placed after all except clauses. If trythe code in the block executes successfully and no exception is thrown, elsethe code block in will be executed.

  4. finally: The statement block will be executed regardless of whether an exception occurs finally, even if there are statements in trythe or exceptblock . Statements are often used to ensure proper cleanup of resources, such as closing files or releasing network connections.returnfinally

  By using these structures properly, programmers can take appropriate measures when exceptions occur to ensure the stability and maintainability of the program and write more robust applications.

  The following is a simple example of file operation exception handling. We try to open the file and exceptcustomize the corresponding exception handling logic in the block:

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("文件操作完成。")

  Within a block, you can also catch specific types of exceptionsexcept if needed . This allows you to handle different types of problems more precisely. For example, if you only care about file non-existence exceptions, you can catch them .FileNotFoundError

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

exceptClauses can specify multiple exceptions as a parenthesized tuple, for example::

except (RuntimeError, TypeError, NameError):
	pass

   finallyis tryan optional clause of the statement, which is used to define cleanup operations that must be performed in all cases (such as releasing external resources such as files), and the clause trywill be executed regardless of whether the statement triggers an exception finally. The following content introduces several more complex trigger exception scenarios:

  • If the finally clause contains break, continue, or return statements, the exception will not be re-raised.

    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
    
  • If a break, continue, or return statement is encountered while executing the try statement, the finally clause is executed before the break, continue, or return statement is executed.

    def example():
        try:
            print("In try block")
            return 10
        finally:
            print("In finally block")
    
    result = example()
    print("Result:", result)  
    
    In try block
    In finally block
    Result: 10
    
  • If the finally clause contains a return statement, the return value comes from the return value of one of the return statements of the finally clause, not from the return statement of the try clause.

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

Here's another complex example:

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'

9.3 Exception classes and exception handling mechanisms

  In Python, exceptions are represented by instances of the exception class. Exception classes are a way to organize and represent different types of exceptions. Each exception class represents a specific type of error or exceptional condition, such as ValueErrornumerical errors. When an exception occurs in a program, Python creates an instance of the exception class associated with the exception and raises it.

Python defines a number of built-in exceptions   for common error conditions to handle errors and exceptional conditions during program execution, and these classes are all inherited from base classes BaseException. The hierarchical structure of the entire exception class is as follows:

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

Some exception classes are explained below:

Main exception categories describe
BaseException The base class of all built-in exception classes , usually not used directly, but derived from other exception classes
GeneratorExit Used to automatically shut down when the generator or coroutine encounters this exception during execution. Typically used to clean up resources in generators or coroutines
KeyboardInterrupt This exception is thrown when the user presses Ctrl+C on the command line, interrupting the executing program.
SystemExit sys.exit()Exception raised when the function is called , used to exit the Python interpreter.
Exception The base class for all built-in non-system exit classes, and all user-defined exceptions should also be derived from this class
Common exception categories describe
SyntaxError Grammatical errors
IndentationError Indentation error
ImportError The imported module does not exist
NameError NameError, raised when trying to access an undefined variable or function.
AttributeError Property error, raised when trying to access a property or method that the object does not have.
TypeError TypeError, raised when operating on an incompatible data type.
KeyError Raised when using a key that does not exist in the dictionary.
ValueError Raised when a function receives a parameter of the correct type but an illegal value.
ZeroDivisionError Division by zero error, raised when trying to divide by zero.
FileNotFoundError File not found error
PermissionError Insufficient file permissions error
IndexError Index error, raised when trying to access a non-existent list element.
MemoryError Memory exhausted exception. Usually occurs when working with large data sets.

  Classes have the characteristic of inheritance, so if the exception that occurs is the same class or its base class as the class in the except clause, then the class is compatible with the exception, and vice versa. To put it simply, it means that the subclass is compatible with the exception of the parent class, and the parent class is not compatible with the exception of the subclass. The following example illustrates:

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

The above code will print B, C, D in sequence. And if the order of the except clauses is changed to:

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

  B,B,B will be printed. This is because C and D are both subclasses of B. If they can catch errors of class B, they will definitely catch errors of class C and class D, so all three traversals will be caught by except B.

9.4 Custom exception classes

  Built-in exception classes can be subclassed to define new exceptions, but when creating custom exception classes, it is recommended to Exceptioninherit from the class or its subclasses, rather than directly inheriting from BaseExceptionthe class. This is because Exceptionis the base class for all nonfatal exceptions, and BaseExceptionthe class and its other subclasses represent very serious exceptions that usually cause the program to terminate and do not need to be handled.

  ExceptionThe class has a constructor __init__(self, *args)that allows one or more parameters (usually an exception message) to be passed. You can extend this constructor in a derived custom exception class to add additional parameters such as error code, timestamp, etc.

  Exception class names generally end with "Error", which is consistent with the naming of standard exceptions. Exception classes should be kept simple, providing only a few properties that allow the appropriate exception handler to extract information about the error. Many standard modules define their own exceptions to report errors that may occur in the functions they define.

  1. Custom properties to provide more contextual information about the error
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 = None

try:
	# 使用 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. Customize methods to perform operations related to exception handling, such as logging exceptions, sending notifications, automatic repairs, etc.
class CustomError(Exception):
    def __init__(self, message, error_code):
        super().__init__(message)
        self.error_code = error_code

    def 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 triggers exceptions and its difference from except

  In addition to using try...exceptstatements, you can also use raisestatements to manually raise specified exceptions, whose only parameter is the exception to be triggered, but it must be an exception instance or exception class.

  1. Raise an exception instance : You can use raisethe statement to raise an instance of an exception class that has already been created. The purpose of this is to raise an exception at a specific code location and provide detailed information about the exception.

    # 创建一个自定义异常类
    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. Throw an exception class : When you want to throw some standard exception, but do not need to provide additional exception information, you can simply throw an exception class.

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

  Sometimes, after catching an exception, we want to continue propagating the same exception to a higher-level exception handler, or let other parts of the code handle it. At this time, we can use the raise statement to re-raise the caught exception :

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

ZeroDivisionError: division by zero

raiseBoth exceptare used to handle exceptions, but their roles and usage scenarios are different:

  • except: Used to catch and handle tryexceptions thrown in blocks. This is a passive operation that occurs in response to an exception. You can exceptdefine exception handling logic in statements, such as specifying different exceptclauses to handle different types of exceptions. If the exception is not handled, the program will terminate and display the exception message.

  • raise: Used to clearly indicate the occurrence of an exception in the code. This is a proactive operation and you can use it anywhere to raise an exception. raiseUsually used as the starting point of an exception, it tells the program that an exception condition has occurred, and then control of the program is passed to the exception handler ( exceptclause).

  Here's a concrete example. Suppose a function, accepting a number as an argument, should raise a custom exception when the argument is negative. Here is raisea usage scenario for which is used to proactively raise exceptions:

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

Then, when calling this function, you can use tryand exceptto catch and handle this exception:

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

  In this example, raiseis used to raise an exception when an unqualified input is detected inside the function, and exceptis used to catch and handle this exception. That's the different uses and roles of raiseand exceptin exception handling

9.6 Exception chain

  Exception chaining refers to the situation in Python where one exception triggers another exception, thus forming a nested chain of exceptions. This chain can be used to trace the source of the exception to better understand what went wrong in the program, especially when the exception occurred in a complex program stack. Exception chains can be better organized and managed through the correct use raiseof the statement's clause. fromThe following are common exception chain operations:

  1. Using fromclauses to trigger exception chaining: You can raiseuse fromclauses in statements to treat one exception as the cause of another exception. This way, the newly raised exception will be the "immediate cause" of the previous exception.

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

  In this example, we first raise an ValueErrorexception and then exceptraise an TypeErrorexception within the block, using the previous exception veas the immediate cause. In this way, we can access the direct cause, that is, the exception TypeErrorthrough in the exception .te.__cause__ValueError

  1. Use from Nonedisable automatic exception chaining : If used in the clause raiseof a statement , it disables Python's automatic creation of exception chains to reduce the complexity of exceptions. At this point, exceptions are considered independent of each other.fromNone

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

    In this example, exception chaining is disabled, ve.__cause__with a value of None, and is no longer associated with other exceptions.

9.7 Handling multiple unrelated exceptions

  In some cases, it may be necessary to report multiple exceptions that have occurred, rather than just reporting the first exception. For example, in situations such as concurrency frameworks, when multiple tasks are executed in parallel, multiple errors may occur.

  In order to handle this situation, Python provides a method ExceptionGroupthat allows multiple exception instances to be packed into a list and thus raised together, which allows multiple exceptions to be caught and handled at the same time.

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 5
      2     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)

  The above code uses raise ExceptionGroupthrows an exception that contains an error message 'there were problems' and the list of exception instances excs created earlier. This creates an exception chain where ExceptionGroup is the top-level exception, and OSError and SystemError are sub-exceptions below it.

Use the following code to print specific exception information:

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

  With a nested exception group, you can use except*the clause to extract a certain type of exception from the group, while letting all other exceptions propagate to other clauses and eventually be re-raised. Also, exceptions nested within an exception group must be instances, not types. This is because in practice these exceptions are usually those that have already been raised and caught by the program. For details of this part, please refer to "Errors and Exceptions" .

9.8 Use comments to refine exceptions

  After an exception is caught, it is sometimes necessary to add additional information to the exception object to better describe the error and provide contextual information. Python's exception objects have add_note(note)methods that allow you to add string comments to the exception object's comments list. When an exception is raised, the annotations in the exception object will be displayed in the standard exception traceback information in the order in which they were added, for example:

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 2
      1 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

You can also add additional exception comments in custom exceptions:

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 / b
        return result
    except ZeroDivisionError as zd_err:
        # 创建自定义异常并添加注释
        custom_err = CustomError("Division error")
        custom_err.add_note("Attempted to divide by zero")
        raise custom_err

try:
    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

  In this example, dividethe function attempts to perform a division operation, but if the division operation fails (divide by zero), it throws a custom exception CustomError. In this custom exception, we add_noteadded two annotations using the method to describe the nature of the error and the context of the error. Finally, in the exception handling code, we loop through the exception object's annotations list to see all added annotations.

9.9 Best Practices for Exception Handling

  When writing Python code, exception handling is a key aspect, it helps to increase the stability and reliability of the code, can handle various unpredictable situations, and increase the robustness of the code. Here are some suggestions:

  1. Catch only exceptions that you know how to handle so you can handle the problem precisely
    In exception handling, you should catch only those exceptions that you know how to handle. Don't catch all exceptions just because they might occur. This can lead to hiding the real problem, making debugging more difficult. Only catch exceptions that you can handle. For other exceptions, let them raise and stop the execution of the program.

  2. Throw a custom exception with a clear description
    When you need to indicate a specific error condition in your code, you should throw a custom exception. These exceptions should have a clear description so that other developers can understand the nature of the problem. For example:

    if condition:
        raise CustomException("This is a custom exception message.")
    
  3. Do not catch Exceptiongeneric exceptions
    Avoid catching generic Exceptionexceptions because this will catch all exceptions, including system exits, etc. This can cause the program to behave unpredictably. Specific exception types should be caught, such as ValueError, FileNotFoundErroretc.

  4. Recording exception information
    When catching an exception, exception information should be recorded for subsequent debugging. You can use modules in the standard library loggingto log exception information to make it easier to diagnose problems. For example:

    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()记录错误级别的日志,包括异常信息	    
    

  In the above code, the format parameter is used to define the format of the log message. It is a string containing placeholders. These placeholders will be replaced with corresponding values ​​when the log message is actually recorded. Commonly used parameters include:

Placeholder meaning
%s Used to insert strings.
%d Used to insert integers.
%f Used to insert floating point numbers.
%r Used to insert a string representing an object, usually repr(object)the result of .
%(name)s Used to insert named variable values, where nameis a variable name.
%(asctime)s Timestamp, usually a string with date and time. asctimeRepresents "human-readable time", usually including date and time.
%(levelname)s Log message level, such as "ERROR", "INFO", etc.
%(message)s The body of the log message.

  Finally, logging.error()this is to call the method in the logging module error, which means recording an error level log message and s%indicating the string format. By setting the above format, the final output log is as follows and is recorded in the myapp.log file:

# 519表示毫秒
2023-09-07 13:36:06,519 [ERROR] An error occurred: division by zero
  1. Use finallyblocks for cleanup operations
    If you have cleanup code that needs to be executed regardless of whether an exception occurs, you can use finallya block, which ensures that the cleanup code will always be executed even if an exception occurs.

Guess you like

Origin blog.csdn.net/qq_56591814/article/details/132626626