Python中的错误处理:最佳实践。探索如何有效处理异常

摘录:
错误是不可恢复的;当我们的程序遇到错误时,它会立即退出或崩溃。

一个好的程序员会确保他们的代码或软件能够优雅地处理可能因使用软件而产生的错误/异常,而不会崩溃或产生错误结果。想象一下,为一家金融机构编写软件,该软件仅接受用户输入的数字,而用户却输入了字母进行算术运算,这通常会引发错误,如果没有适当的机制来处理该错误,软件就会因为一个用户的输入而崩溃。这绝对不是一件好事——这对业务有害,可能会导致客户感到沮丧,甚至可能有人因为无能而失去工作。

在本文中,我们将学习如何以最佳方式处理由于用户交互而可能在我们的代码中出现的错误。

前提条件

本文适合:

  • 希望学习如何在代码中处理异常的Python软件开发人员。
  • 已经熟悉Python的人,希望了解Python中的错误处理概念。
  • 希望提升现有Python错误处理知识的专业人士。

目标

通过阅读本文,读者应该能够:

    • 清楚理解Python中错误处理的概念及其重要性。

 

    • 了解自定义异常类及其实现方法。

 

    • 理解Python中错误和异常之间的关键区别。

解释错误和异常

错误异常通常可以互换使用,但它们在技术上意味着不同的东西。在Python中,错误异常都是BaseException的子类,这进一步表明它们虽然不同,但有共同的相似之处。

错误

错误是不可恢复的;当我们的程序遇到错误时,它会立即退出或崩溃。即使你可能预期会出现错误,也没有程序化处理错误的余地。以下是一些错误的例子:

语法错误(SyntaxError):

这是程序员面临的最常见错误之一,它发生在代码未遵循正确的Python语法时,并且可以在解析过程中被检测到。这对于学习者或从其他编程语言转向Python的人来说,尤其容易出现问题。

name = “Austin”;
print(name)

这会导致SyntaxError,因为在Python中,语句不以分号结束。

IndentationError:

当Python代码没有正确缩进时,会出现此错误,并在代码解析时被检测到。缩进在Python中非常重要。它是定义代码块的唯一方式,而与大多数使用大括号的语言不同。

active = True
if (active):
print(“The user is active”)

这段代码将因为缩进不当而导致错误。它应该是:

if (active):
    print(“The user is active”)

 

异常

Python中的异常发生在运行时。与错误不同,异常可以被程序捕获或处理,从而使程序能够继续运行而不会崩溃。换句话说,它们是可恢复的。以下是Python中一些常见的异常:

内置异常

这些类型的异常是Python编程语言的一部分。以下是其中的一些:

值错误(ValueError):

当一个无效的参数被传递给函数时,即使类型可能是正确的,也会发生这种情况。


def str_num(num_string):
    return(int(string))

从上面的代码片段来看,如果我们将一个数字字符串传递给该函数,它将成功转换为数字,否则将引发一个 ValueError。


print(str_num(“123”)) #运行正常
print(str_num(“abc”)) #引发 ValueError 

类型错误(TypeError):

当传递给函数的参数类型不合适时,会引发此错误。

def addition(num1, num2):
    return num1 + num2
# 调用函数
addition(5, A)

这会引发一个类型错误(TypeError)。

索引错误(IndexError):

当你尝试使用错误的索引访问列表中的值时,就会出现这种情况。

my_list = [“dog”, “cat”]
my_list[2]

这将导致 IndexError,因为 my_list[2] 是不可访问的。

KeyError:

当尝试使用错误或不存在的键访问字典中的值时,会引发此错误。

my_dict = {“name”: “Austin”, “occupation”: “Software Engineer”}
my_dict[“age”]

 

这会引发一个 KeyError。
您可以在 这里 找到其他内置异常。

自定义异常

自定义异常由程序员定义。在这里,Python 使得手动定义程序在执行期间应检查的条件成为可能,并在发现时引发异常。您可以通过创建一个继承自 Exception 类的类来实现这一点。

处理异常

处理异常确保我们的软件应用在遇到某些在应用生命周期中出现的错误时具有可预测的性能。在本节中,您将学习如何通过编程方式实现这一点。

使用 try-except 语句

try-except 语句提供了一种安全的方式来处理可能引发错误或异常的代码。try 语句包裹了有问题的代码或try 子句; 这是最有可能导致整个程序出现错误的代码部分; 这种情况通常发生在接收用户输入、读取文件等场景中。

except 语句标志着except 子句的开始; 这是包裹在 except 块中的剩余代码。except 子句 处理在try 块中引发的异常。

让我带你了解执行工作流程。你的Python程序通常会执行到try块,在那里你的“问题”代码很可能会出现。如果在执行try块中的代码时没有可能的错误,它将跳过except块并继续执行代码的其余部分。但如果在执行try块中的代码时遇到错误,将会创建一个异常对象,控制权会立即跳转到except块,在那里应该由匹配的异常类来处理。

try:
    age = int(input(“请输入你的年龄”))
    print(f“你出生于 {age} 年前”)

except ValueError:
    print(“引发了ValueError,请仅输入数字”)

从上面的代码片段来看,如果程序传入一个非数字值,就会创建一个异常对象并引发一个 ValueError。控制流会立即跳转到 except 块,在那里它会扫描合适的异常类。在这里,ValueError 类就足够了。错误得到了妥善处理。但如果没有找到合适的类,控制流会转移到外层的 try 块(如果有的话),如果异常仍未得到妥善处理,程序将崩溃。

使用一个 except 语句处理多个异常类

可以检查多个异常类,并处理特定的异常。如果你不确定在代码执行过程中会产生哪种异常,这种方法是首选。请参见下面的代码片段。

except (RuntimeError, TypeError, NameError):
    pass

 

通配符异常类

异常类是BaseException的直接子类。异常类是所有非致命异常的基类。
大多数情况下,异常类足以处理代码执行过程中引发的大多数异常。

try:
    # 可能引发异常的代码
except Exception:
    # 处理异常

尽管异常类能够处理非致命异常,但最好还是谨慎使用。使用合适的异常类,这对调试和提高代码可读性更有益。

使用 finally 语句

包裹在 finally 块中的代码无论是否发生异常都会执行。这使得它适合处理清理任务,例如关闭文件和释放内存资源。


try:
    # 可能引发异常的代码
except SomeException:
    # 处理异常
else:
    # 可选:如果没有异常发生则执行
finally:
    # 无论如何总是执行的代码

 

创建自定义异常类

创建自定义异常使程序员能够为软件程序设计特定的异常。这些异常可以涉及可能对特定软件程序的功能产生不利影响的特殊条件或边缘情况。定义的自定义异常类必须继承自 Exception 类。

class InvalidAgeError(Exception):
    pass

def check_age(age):
    if age < 0:
        raise InvalidAgeError("年龄不能为负数。")

try:
    check_age(-5)
except InvalidAgeError as e:
    print(f"错误: {e}")

上面的代码片段展示了如何创建和使用自定义异常。根据使用场景,它可以以更复杂的方式使用。

为什么错误/异常处理很重要

“不要相信用户”是软件开发人员之间常说的一句话,这重申了你无法完全确定用户将如何与您的软件应用程序交互;他们输入的是什么类型的数据,以及一些其他边缘情况,这些情况可能是你作为程序员在编写应用程序时没有考虑到的。以下是一些正确处理错误/异常的重要原因:

  1. 防止崩溃没有异常处理时,未处理的错误可能会导致程序突然崩溃。这可能导致数据丢失或用户体验不佳。
    示例:
    没有异常处理:
print(10 / 0)  # ZeroDivisionError: division by zero

有异常处理时:

try:
    print(10 / 0)
except ZeroDivisionError:
    print("无法被零除!")

 

  1. 改善用户体验
    妥善处理的异常提供有意义的错误信息,而不是晦涩的系统错误,从而保持应用程序的用户友好性。
    示例:
try:
    age = int(input("请输入您的年龄: "))
except ValueError:
    print("输入无效!请您输入一个数字。")
  1. 保持应用程序稳定性
    它允许应用程序在遇到错误后继续运行,从而确保稳定性。
    示例:
def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "不允许除以零!"

print(divide(10, 2))  # 输出: 5.0
print(divide(10, 0))  # 输出: 不允许除以零!
  1. 处理边缘情况
    异常处理让你能够考虑不可预测的场景,例如网络故障、缺失文件或无效的用户输入。
    示例:
try:
    with open("data.txt", "r") as file:
        content = file.read()
except FileNotFoundError:
    print("未找到该文件。")
  1. 鼓励清晰的代码
    通过将正常逻辑与错误处理逻辑分开,异常处理使你的代码更易于阅读、调试和维护。
  2. 促进调试
    通过详细的错误信息和异常日志,开发者可以快速识别并修复代码中的问题。
    示例:
import logging

try:
    result = 10 / 0
except Exception as e:
    logging.error("发生错误", exc_info=True)
  1. 对关键系统至关重要
    在可靠性至关重要的系统中(例如银行、医疗保健),异常处理是必要的,以确保安全地管理错误,而不会导致数据损坏或丢失。

结论

在编程中,错误和异常通常是可以互换使用的。Python中错误和异常的关键区别在于它们对我们软件程序的影响。像语法错误和缩进错误这样的错误会在解释器解析程序时导致程序崩溃。而异常则是在运行时如果没有得到妥善处理,会导致程序崩溃。自定义异常通过使程序员能够定义特定于某个软件应用程序的异常类,扩展了错误处理的能力。

错误处理对于调试也非常重要。它使我们能够看到应用程序中错误发生的原因,为软件维护者提供足够的信息来修复问题。始终确保在代码中适当地引入异常处理,以确保软件应用程序的健壮性。

感谢您的阅读。

更多